CView :: OnFilePrint崩溃MFC应用程序

时间:2018-02-06 21:35:31

标签: visual-c++ mfc

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

我们使用非常标准的MFC代码来启动打印。我们在代码的相关区域周围添加了try/catch块,没有运气。无论什么失败似乎都没有。

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

Windows事件记录器显示我们的应用程序是Faulting应用程序。

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

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

我们认为CDC::StartDoc将是下一个被调用的东西,然后是CView::OnPrepareDC。我们失败时,我们无法前往OnPrepareDC

我们似乎找不到CView::OnFilePrint的源代码,因此我们不确定它的外观。通过在线研究,我们认为事情按照OnFilePrint

的顺序发生
// 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,因为它的价值。

2 个答案:

答案 0 :(得分:1)

CView::OnFilePrint的源代码应该在C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\src\mfc\viewprnt.cpp中,具体取决于VS版本。打印机初始化/访问也可能存在问题。

如果有任何错误,很可能是由于打印机初始化造成的。您可以覆盖OnFilePrint并添加CPrintInfo printInfo进行测试。例如:

//ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
//ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT, OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, OnFilePrint)

void CMyView::OnFilePrint()
{
    try
    {
        CPrintInfo printInfo;
    }
    catch(...)
    {
        //log error
        AfxMessageBox(L"error");
    }

    CView::OnFilePrint();
}

如评论中所述,另一种可能性是代码中的其他地方存在错误,这可能不一定与打印有关。

答案 1 :(得分:1)

在CView :: OnFilePrint内部,会发生这种情况:

CWnd * hwndTemp = AfxGetMainWnd();

事实证明,如果你不从主线程调用OnFilePrint,则返回NULL。

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

hwndTemp->EnableWindow(FALSE);

应用程序崩溃。

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

CWnd * hwndTemp = AfxGetApp()->GetMainWnd();

取代这个:

CWnd * hwndTemp = AfxGetMainWnd();

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

CView :: OnFilePrint中代码追逐版的剪切在这里:

    // 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代码,注明问题区域:

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
    }
}