首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >CView::OnFilePrint崩溃MFC应用程序

CView::OnFilePrint崩溃MFC应用程序
EN

Stack Overflow用户
提问于 2018-02-06 21:35:30
回答 1查看 1.3K关注 0票数 1

我们已经使用和维护了许多年的MFC应用程序。最近,我们对运行该应用程序的一些计算机进行了一些管理更改。现在,当从应用程序打印时,软件偶尔会崩溃。

我们正在使用相当标准的MFC代码来启动打印。我们在代码的相关区域周围添加了try/catch块,没有任何进展。任何失败的东西似乎都不会抛出。

我们得到一个典型的对话框,说明"____ MFC应用程序已停止工作“。关闭程序是唯一的选择。

windows事件记录器显示我们的应用程序是错误应用程序。

异常代码是0xc0000005,它似乎是访问被拒绝的错误。

当崩溃发生时,应用程序在CView::OnFilePrint()代码中。我们增加了一些日志记录,我们知道我们通过了DoPreparePrintingOnBeginPrinting

我们相信CDC::StartDoc是下一个叫做CView::OnPrepareDC的东西。当我们失败的时候,我们不会到达OnPrepareDC

我们似乎没有找到CView::OnFilePrint的源代码,所以我们不确定它是什么样子。从网上的研究来看,我们认为在OnFilePrint中发生的事情是这样的。

代码语言:javascript
复制
// what we think is in OnFilePrint:
CView::OnFilePrint()
{
    OnPreparePrinting();   <- we get through our override of this
    OnBeginPrinting();     <- we get through our override of this

    // loop back to here on multiple docs
    CDC::StartDoc();
    CView::OnPrepareDC();  <- we do not reach our override of this
    CView::OnPaint();
    CDC::EndPage();
    // loop back on multiple docs
    ...
    // finish if last doc...
}

我想要它的来源,以便我们可以尝试重写它,并试图优雅地失败,而不是失败的崩溃。

我在找:

1)任何关于如何找出为什么打印过程导致我们的应用程序崩溃的建议。

2) CView::OnFilePrint代码所在的位置(如果可用的话)。(为了缩小问题的范围,我留下的唯一想法是调用我们自己的版本,这样我们就可以逐步完成它,并添加日志记录和/或查看问题发生时是否至少可以优雅地失败。)

这台打印机是Xerox Phaser 3610,价值不菲。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-02-09 00:31:33

在CView内部::OnFilePrint,这会发生:

代码语言:javascript
复制
CWnd * hwndTemp = AfxGetMainWnd();

结果是,如果不从主线程调用OnFilePrint,则返回NULL。

由于计算机登录到域时时间稍有更改,因此正在从另一个线程调用OnFilePrint。这将导致上面的调用返回null,然后在执行该行时:

代码语言:javascript
复制
hwndTemp->EnableWindow(FALSE);

应用程序崩溃。

有几种方法可以解决这个问题。一是使用这一方法:

代码语言:javascript
复制
CWnd * hwndTemp = AfxGetApp()->GetMainWnd();

取而代之的是:

代码语言:javascript
复制
CWnd * hwndTemp = AfxGetMainWnd();

另一种方法是确保只从主线程调用OnFilePrint

CView::OnFilePrint中代码的chase版本的切分如下:

代码语言:javascript
复制
    // disable main window while printing & init printing status dialog
    // Store the Handle of the Window in a temp so that it can be enabled 
    // once the printing is finished
    CWnd * hwndTemp = AfxGetMainWnd();      // <--- CAN RETURN NULL HERE
    hwndTemp->EnableWindow(FALSE);          // <--- CRASH WILL OCCUR HERE
    CPrintingDialog dlgPrintStatus(this);

CView的完整版本::OnFilePrint在下面。

OnFilePrint代码,并注意到问题区域:

代码语言:javascript
复制
void CView::OnFilePrint()
{
    // get default print info
    CPrintInfo printInfo;
    ASSERT(printInfo.m_pPD != NULL);    // must be set

    if (LOWORD(GetCurrentMessage()->wParam) == ID_FILE_PRINT_DIRECT)
    {
        CCommandLineInfo* pCmdInfo = AfxGetApp()->m_pCmdInfo;

        if (pCmdInfo != NULL)
        {
            if (pCmdInfo->m_nShellCommand == CCommandLineInfo::FilePrintTo)
            {
                printInfo.m_pPD->m_pd.hDC = ::CreateDC(pCmdInfo->m_strDriverName,
                    pCmdInfo->m_strPrinterName, pCmdInfo->m_strPortName, NULL);
                if (printInfo.m_pPD->m_pd.hDC == NULL)
                {
                    AfxMessageBox(AFX_IDP_FAILED_TO_START_PRINT);
                    return;
                }
            }
        }

        printInfo.m_bDirect = TRUE;
    }

    if (OnPreparePrinting(&printInfo))
    {
        // hDC must be set (did you remember to call DoPreparePrinting?)
        ASSERT(printInfo.m_pPD->m_pd.hDC != NULL);

        // gather file to print to if print-to-file selected
        CString strOutput;
        if (printInfo.m_pPD->m_pd.Flags & PD_PRINTTOFILE && !printInfo.m_bDocObject)
        {
            // construct CFileDialog for browsing
            CString strDef(MAKEINTRESOURCE(AFX_IDS_PRINTDEFAULTEXT));
            CString strPrintDef(MAKEINTRESOURCE(AFX_IDS_PRINTDEFAULT));
            CString strFilter(MAKEINTRESOURCE(AFX_IDS_PRINTFILTER));
            CString strCaption(MAKEINTRESOURCE(AFX_IDS_PRINTCAPTION));
            CFileDialog dlg(FALSE, strDef, strPrintDef,
                OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, strFilter, NULL, 0);
            dlg.m_ofn.lpstrTitle = strCaption;

            if (dlg.DoModal() != IDOK)
                return;

            // set output device to resulting path name
            strOutput = dlg.GetPathName();
        }

        // set up document info and start the document printing process
        CString strTitle;
        CDocument* pDoc = GetDocument();
        if (pDoc != NULL)
            strTitle = pDoc->GetTitle();
        else
            EnsureParentFrame()->GetWindowText(strTitle);
        DOCINFO docInfo;
        memset(&docInfo, 0, sizeof(DOCINFO));
        docInfo.cbSize = sizeof(DOCINFO);
        docInfo.lpszDocName = strTitle;
        CString strPortName;
        if (strOutput.IsEmpty())
        {
            docInfo.lpszOutput = NULL;
            strPortName = printInfo.m_pPD->GetPortName();
        }
        else
        {
            docInfo.lpszOutput = strOutput;
            AfxGetFileTitle(strOutput,
                strPortName.GetBuffer(_MAX_PATH), _MAX_PATH);
        }

        // setup the printing DC
        CDC dcPrint;
        if (!printInfo.m_bDocObject)
        {
            dcPrint.Attach(printInfo.m_pPD->m_pd.hDC);  // attach printer dc
            dcPrint.m_bPrinting = TRUE;
        }
        OnBeginPrinting(&dcPrint, &printInfo);

        if (!printInfo.m_bDocObject)
            dcPrint.SetAbortProc(_AfxAbortProc);

/**********************************************************************
    Problem area.

    If the calling thread is not the main thread, the call to AfxGetMainWnd
    can return NULL.  In this case, hwndTemp->EnableWindow(FALSE) will crash
    the application.
**********************************************************************/

        // disable main window while printing & init printing status dialog
        // Store the Handle of the Window in a temp so that it can be enabled 
        // once the printing is finished
        CWnd * hwndTemp = AfxGetMainWnd();      // <--- CAN RETURN NULL HERE
        hwndTemp->EnableWindow(FALSE);          // <--- CRASH WILL OCCUR HERE
        CPrintingDialog dlgPrintStatus(this);

        CString strTemp;
        dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_DOCNAME, strTitle);
        dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_PRINTERNAME,
            printInfo.m_pPD->GetDeviceName());
        dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_PORTNAME, strPortName);
        dlgPrintStatus.ShowWindow(SW_SHOW);
        dlgPrintStatus.UpdateWindow();

        // start document printing process
        if (!printInfo.m_bDocObject)
        {
            printInfo.m_nJobNumber = dcPrint.StartDoc(&docInfo);
            if (printInfo.m_nJobNumber == SP_ERROR)
            {
                // enable main window before proceeding
                hwndTemp->EnableWindow(TRUE);

                // cleanup and show error message
                OnEndPrinting(&dcPrint, &printInfo);
                dlgPrintStatus.DestroyWindow();
                dcPrint.Detach();   // will be cleaned up by CPrintInfo destructor
                AfxMessageBox(AFX_IDP_FAILED_TO_START_PRINT);
                return;
            }
        }

        // Guarantee values are in the valid range
        UINT nEndPage = printInfo.GetToPage();
        UINT nStartPage = printInfo.GetFromPage();

        if (nEndPage < printInfo.GetMinPage())
            nEndPage = printInfo.GetMinPage();
        if (nEndPage > printInfo.GetMaxPage())
            nEndPage = printInfo.GetMaxPage();

        if (nStartPage < printInfo.GetMinPage())
            nStartPage = printInfo.GetMinPage();
        if (nStartPage > printInfo.GetMaxPage())
            nStartPage = printInfo.GetMaxPage();

        int nStep = (nEndPage >= nStartPage) ? 1 : -1;
        nEndPage = (nEndPage == 0xffff) ? 0xffff : nEndPage + nStep;

        VERIFY(strTemp.LoadString(AFX_IDS_PRINTPAGENUM));

        // If it's a doc object, we don't loop page-by-page
        // because doc objects don't support that kind of levity.

        BOOL bError = FALSE;
        if (printInfo.m_bDocObject)
        {
            OnPrepareDC(&dcPrint, &printInfo);
            OnPrint(&dcPrint, &printInfo);
        }
        else
        {
            // begin page printing loop
            for (printInfo.m_nCurPage = nStartPage;
                printInfo.m_nCurPage != nEndPage; printInfo.m_nCurPage += nStep)
            {
                OnPrepareDC(&dcPrint, &printInfo);

                // check for end of print
                if (!printInfo.m_bContinuePrinting)
                    break;

                // write current page
                TCHAR szBuf[80];
                ATL_CRT_ERRORCHECK_SPRINTF(_sntprintf_s(szBuf, _countof(szBuf), _countof(szBuf) - 1, strTemp, printInfo.m_nCurPage));

                dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_PAGENUM, szBuf);

                // set up drawing rect to entire page (in logical coordinates)
                printInfo.m_rectDraw.SetRect(0, 0,
                    dcPrint.GetDeviceCaps(HORZRES),
                    dcPrint.GetDeviceCaps(VERTRES));
                dcPrint.DPtoLP(&printInfo.m_rectDraw);

                // attempt to start the current page
                if (dcPrint.StartPage() < 0)
                {
                    bError = TRUE;
                    break;
                }

                // must call OnPrepareDC on newer versions of Windows because
                // StartPage now resets the device attributes.
                OnPrepareDC(&dcPrint, &printInfo);

                ASSERT(printInfo.m_bContinuePrinting);

                // page successfully started, so now render the page
                OnPrint(&dcPrint, &printInfo);
                if ((nStep > 0) && // pages are printed in ascending order
                    (nEndPage > printInfo.GetMaxPage() + nStep)) // out off pages
                {
                    // OnPrint may have set the last page
                    // because the end of the document was reached.
                    // The loop must not continue with the next iteration.
                    nEndPage = printInfo.GetMaxPage() + nStep; 
                }

                // If the user restarts the job when it's spooling, all 
                // subsequent calls to EndPage returns < 0. The first time
                // GetLastError returns ERROR_PRINT_CANCELLED
                if (dcPrint.EndPage() < 0 && (GetLastError()!= ERROR_SUCCESS))
                {
                    HANDLE hPrinter;
                    if (!OpenPrinter(LPTSTR(printInfo.m_pPD->GetDeviceName().GetBuffer()), &hPrinter, NULL))
                    {
                        bError = TRUE;
                        break;
                    }

                    DWORD cBytesNeeded;
                    if(!GetJob(hPrinter,printInfo.m_nJobNumber,1,NULL,0,&cBytesNeeded))
                    {
                        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
                        {
                           bError = TRUE;
                           break;
                        }
                    }

                    JOB_INFO_1 *pJobInfo;                   
                    if((pJobInfo = (JOB_INFO_1 *)malloc(cBytesNeeded))== NULL)
                    {
                        bError = TRUE;
                        break;
                    }

                    DWORD cBytesUsed;

                    BOOL bRet = GetJob(hPrinter,printInfo.m_nJobNumber,1,LPBYTE(pJobInfo),cBytesNeeded,&cBytesUsed);

                    DWORD dwJobStatus = pJobInfo->Status;

                    free(pJobInfo);
                    pJobInfo = NULL;

                    // if job status is restart, just continue
                    if(!bRet || !(dwJobStatus & JOB_STATUS_RESTART) )
                    {
                        bError = TRUE;
                        break;
                    }
                }

                if(!_AfxAbortProc(dcPrint.m_hDC, 0))
                {       
                    bError = TRUE;
                    break;
                }
            }
        }

        // cleanup document printing process
        if (!printInfo.m_bDocObject)
        {
            if (!bError)
                dcPrint.EndDoc();
            else
                dcPrint.AbortDoc();
        }

        hwndTemp->EnableWindow();    // enable main window

        OnEndPrinting(&dcPrint, &printInfo);    // clean up after printing
        dlgPrintStatus.DestroyWindow();

        dcPrint.Detach();   // will be cleaned up by CPrintInfo destructor
    }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48652342

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档