I have a simple text editor that I created in Visual Studio 2010 Professional Edition. Basically I modified the MFC MDI program automatically generated by the VS2010 wizard. The problem is that when i print, it gives me a debug assertion error in viewrich.cpp line 294. I have not modified anything in the code to do with printing, though it could be something wrong with how i used Rich Edit. This is all the information I have. Thanks in advance.
Viewrich.cpp
BOOL CRichEditView::PaginateTo(CDC* pDC, CPrintInfo* pInfo)
// attempts pagination to pInfo->m_nCurPage, TRUE == success
{
ASSERT_VALID(this);
ASSERT_VALID(pDC);
CRect rectSave = pInfo->m_rectDraw;
UINT nPageSave = pInfo->m_nCurPage;
ASSERT(nPageSave > 1); // LINE 294
ASSERT(nPageSave >= (UINT)m_aPageStart.GetSize());
VERIFY(pDC->SaveDC() != 0);
pDC->IntersectClipRect(0, 0, 0, 0);
pInfo->m_nCurPage = (int)m_aPageStart.GetSize();
while (pInfo->m_nCurPage < nPageSave)
{
ASSERT(pInfo->m_nCurPage == (UINT)m_aPageStart.GetSize());
OnPrepareDC(pDC, pInfo);
ASSERT(pInfo->m_bContinuePrinting);
pInfo->m_rectDraw.SetRect(0, 0,
pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
pDC->DPtoLP(&pInfo->m_rectDraw);
OnPrint(pDC, pInfo);
if (pInfo->m_nCurPage == (UINT)m_aPageStart.GetSize())
break;
++pInfo->m_nCurPage;
}
BOOL bResult = pInfo->m_nCurPage == nPageSave;
pDC->RestoreDC(-1);
pInfo->m_nCurPage = nPageSave;
pInfo->m_rectDraw = rectSave;
ASSERT_VALID(this);
return bResult;
}
EmergenceView.cpp
IMPLEMENT_DYNCREATE(CEmergenceView, CRichEditView)
BEGIN_MESSAGE_MAP(CEmergenceView, CRichEditView)
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, &CRichEditView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, &CRichEditView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CEmergenceView::OnFilePrintPreview)
ON_WM_CONTEXTMENU()
ON_WM_RBUTTONUP()
ON_COMMAND(ID_MUTATE_GROUP, &CEmergenceView::OnMutateGroup)
ON_UPDATE_COMMAND_UI(ID_MUTATE_GROUP, &CEmergenceView::OnUpdateMutateGroup)
ON_COMMAND(ID_MUTATE_RANDOMISE, &CEmergenceView::OnMutateRandomise)
ON_UPDATE_COMMAND_UI(ID_MUTATE_RANDOMISE, &CEmergenceView::OnUpdateMutateRandomise)
ON_COMMAND(ID_HELP_STATISTICS, &CEmergenceView::OnHelpStatistics)
ON_UPDATE_COMMAND_UI(ID_HELP_STATISTICS, &CEmergenceView::OnUpdateHelpStatistics)
ON_COMMAND(ID_MUTATE_POETRIZE, &CEmergenceView::OnMutatePoetrize)
ON_COMMAND(ID_EDIT_SELECTALL, &CEmergenceView::OnEditSelectall)
END_MESSAGE_MAP()
// CEmergenceView construction/destruction
CEmergenceView::CEmergenceView()
{
// TODO: add construction code here
}
CEmergenceView::~CEmergenceView()
{
}
BOOL CEmergenceView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
return CRichEditView::PreCreateWindow(cs);
}
// CEmergenceView drawing
void CEmergenceView::OnDraw(CDC* /*pDC*/)
{
CEmergenceDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: add draw code for native data here
}
// CEmergenceView printing
void CEmergenceView::OnFilePrintPreview()
{
#ifndef SHARED_HANDLERS
AFXPrintPreview(this);
#endif
}
BOOL CEmergenceView::OnPreparePrinting(CPrintInfo* pInfo)
{
// default preparation
return DoPreparePrinting(pInfo);
}
void CEmergenceView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add extra initialization before printing
}
void CEmergenceView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add cleanup after printing
}
The ASSERT says it all:
UINT nPageSave = pInfo->m_nCurPage;
ASSERT(nPageSave > 1);
This is a value for the current page to print/paginate. It is set to 1 in CPrintInfo's constructor. But something changed it and made it 0 or negative. Usually this value is completely controlled by the RTF printout. So you must done something that manipulates it.
You have to set the Minimum page and the Maximum page value (SetMinPage and SetMaxPage) in CPrintInfo.
Related
I have come up with the following function which works as it should:
bool CChristianLifeMinistryStudentMaterialDlg::EncodeText(HWND hWnd, CString strCode)
{
bool bHandled = false;
map<HWND, CComboBox*> mapControls;
map<HWND, CString*> mapControlsText;
mapControls.emplace(m_cbMaterialAssignment1.GetSafeHwnd(), &m_cbMaterialAssignment1);
mapControls.emplace(m_cbMaterialAssignment2.GetSafeHwnd(), &m_cbMaterialAssignment2);
mapControls.emplace(m_cbMaterialAssignment3.GetSafeHwnd(), &m_cbMaterialAssignment3);
mapControls.emplace(m_cbMaterialAssignment4.GetSafeHwnd(), &m_cbMaterialAssignment4);
mapControlsText.emplace(m_cbMaterialAssignment1.GetSafeHwnd(), &m_strMaterialAssignment1);
mapControlsText.emplace(m_cbMaterialAssignment2.GetSafeHwnd(), &m_strMaterialAssignment2);
mapControlsText.emplace(m_cbMaterialAssignment3.GetSafeHwnd(), &m_strMaterialAssignment3);
mapControlsText.emplace(m_cbMaterialAssignment4.GetSafeHwnd(), &m_strMaterialAssignment4);
if (mapControls.find(::GetParent(hWnd)) != mapControls.end())
{
UpdateData(TRUE);
DWORD dwSel = mapControls[::GetParent(hWnd)]->GetEditSel();
CMeetingScheduleAssistantApp::EncodeText(*mapControlsText[::GetParent(hWnd)],
strCode, LOWORD(dwSel), HIWORD(dwSel));
UpdateData(FALSE);
bHandled = true;
}
else
{
map<HWND, CEdit*> mapControls;
map<HWND, CString*> mapControlsText;
mapControls.emplace(m_editBibleReading.GetSafeHwnd(), &m_editBibleReading);
mapControls.emplace(m_editDiscussionVideoTheme.GetSafeHwnd(), &m_editDiscussionVideoTheme);
mapControls.emplace(m_editDiscussionVideoMaterial.GetSafeHwnd(), &m_editDiscussionVideoMaterial);
mapControlsText.emplace(m_editBibleReading.GetSafeHwnd(), &m_strBibleReading);
mapControlsText.emplace(m_editDiscussionVideoTheme.GetSafeHwnd(), &m_strDiscussionVideoTheme);
mapControlsText.emplace(m_editDiscussionVideoMaterial.GetSafeHwnd(), &m_strDiscussionVideoMaterial);
if (mapControls.find(hWnd) != mapControls.end())
{
UpdateData(TRUE);
DWORD dwSel = mapControls[hWnd]->GetSel();
CMeetingScheduleAssistantApp::EncodeText(*mapControlsText[hWnd],
strCode, LOWORD(dwSel), HIWORD(dwSel));
UpdateData(FALSE);
bHandled = true;
}
}
return bHandled;
}
The code is straight forward to follow. But as you can see I have to potentially deal with either a comb box edit control or regular edit control. As a result, I have two sets of similar code.
Is it possible to consolidate some of this code without overcomplicating it too much? My project is set to the ISO C++ 17 Standard if that helps.
Update
Initially I thought I would try a single map of CWnd* pointers. But then I had the two problems of CComboBox verses CEdit.
CComboBox uses:
::GetParent(hWnd)
GetEditSel()
CEdit uses:
hWnd
GetSel()
By using a single list of CWnd* I no longer know which is a combo or a edit control.
—-
Update
The core problem I would like to solve is to have a single loop rather than the two.
One possibility would be to define an interface to the functionality you need, then a couple of implementations of that functionality. Add in a map to get from an HWND to the object you need, and you're off to the races:
class Writer {
virtual DWORD getSel() = 0;
CString* data;
public:
Writer(CString *data) : data(data) {}
void write(CString text) {
UpdateData(true);
DWORD selection = getSel();
CMeetingScheduleAssistantApp::EncodeText(*data, text, LOWORD(dwSel), HIWORD(dwSel));
UpdateData(false);
}
virtual ~Writer() = default;
};
class ComboBoxWriter : public writer {
CWnd *parent;
DWORD getSel() override { return parent->GetEditSel(); }
public:
ComboBoxWriter(CComboBox &dest, CString &data) : Writer(&data), parent(dest.GetParent()) {}
};
class EditCtrlWriter : public Writer {
CEdit *ctrl;
DWORD getSel() override { return ctrl->GetSel(); }
public:
EditCtrlWriter(CEdit &ctrl, CString &data) : Writer(&data), ctrl(&ctrl) {}
};
bool CChristianLifeMinistryStudentMaterialDlg::EncodeText(HWND hWnd, CString strCode) {
static std::map<HWND, Writer*> controls {
{ m_cbMaterialAssignment1.GetSafeHwnd(), new ComboBoxWriter(&m_cbMaterialAssignment1, &m_strMaterialAssignment1) },
{ m_cbMaterialAssignment2.GetSafeHwnd(), new ComboBoxWriter(&m_cbMaterialAssignment2, &m_strMaterialAssignment2) },
{ m_cbMaterialAssignment3.GetSafeHwnd(), new ComboBoxWriter(&m_cbMaterialAssignment3, &m_strMaterialAssignment3) },
{ m_cbMaterialAssignment4.GetSafeHwnd(), new ComboBoxWriter(&m_cbMaterialAssignment4, &m_strMaterialAssignment4) },
{ m_editBibleReading.GetSafeHwnd(), new EditCtrlWriter(&m_editBibleReading, &m_strBibleReading) },
{ m_editDiscussionVideoTheme.GetSafeHwnd(), new EditCtrlWriter(&m_editDiscussionVideoTheme, &m_strDiscussionVideoTheme) },
{ m_editDiscussionVideoMaterial.GetSafeHwnd(), new EditCtrlWriter(&m_editDiscussionVideoMaterial, &m_strDiscussionVideoMaterial) }
};
auto ctrl = controls.find(hwnd);
if (ctrl == controls.end())
return false;
ctrl->second->write(strCode);
return true;
}
This isn't a lot shorter overall (in fact, it's almost the same length), but quite a bit more of that length is boilerplate that's pretty easy to ignore.
Your building maps of HWND to MFC controls looks very strange.
Surely MFC already has that map. See if this helps: https://learn.microsoft.com/en-us/cpp/mfc/accessing-run-time-class-information?view=msvc-160
Based on the comments to one of the answers (#vlad-feinstein):
all HWND handles are unique system-wide
I decided that I could take a much simpler approach to simplifying my code:
bool CChristianLifeMinistryStudentMaterialDlg::EncodeText(HWND hWnd, CString strCode)
{
map<HWND, CWnd*> mapControls; // Use generic CWnd pointers
map<HWND, CString*> mapControlsText;
// Lookup map of controls
mapControls.emplace(m_cbMaterialAssignment1.GetSafeHwnd(), &m_cbMaterialAssignment1);
mapControls.emplace(m_cbMaterialAssignment2.GetSafeHwnd(), &m_cbMaterialAssignment2);
mapControls.emplace(m_cbMaterialAssignment3.GetSafeHwnd(), &m_cbMaterialAssignment3);
mapControls.emplace(m_cbMaterialAssignment4.GetSafeHwnd(), &m_cbMaterialAssignment4);
mapControls.emplace(m_editBibleReading.GetSafeHwnd(), &m_editBibleReading);
mapControls.emplace(m_editDiscussionVideoTheme.GetSafeHwnd(), &m_editDiscussionVideoTheme);
mapControls.emplace(m_editDiscussionVideoMaterial.GetSafeHwnd(), &m_editDiscussionVideoMaterial);
// Lookup map of text values
mapControlsText.emplace(m_cbMaterialAssignment1.GetSafeHwnd(), &m_strMaterialAssignment1);
mapControlsText.emplace(m_cbMaterialAssignment2.GetSafeHwnd(), &m_strMaterialAssignment2);
mapControlsText.emplace(m_cbMaterialAssignment3.GetSafeHwnd(), &m_strMaterialAssignment3);
mapControlsText.emplace(m_cbMaterialAssignment4.GetSafeHwnd(), &m_strMaterialAssignment4);
mapControlsText.emplace(m_editBibleReading.GetSafeHwnd(), &m_strBibleReading);
mapControlsText.emplace(m_editDiscussionVideoTheme.GetSafeHwnd(), &m_strDiscussionVideoTheme);
mapControlsText.emplace(m_editDiscussionVideoMaterial.GetSafeHwnd(), &m_strDiscussionVideoMaterial);
// Determine if the use clicked on a combo or edit control
bool bIsComboControl = false;
HWND hWndToUse = nullptr;
if (mapControls.find(::GetParent(hWnd)) != mapControls.end())
{
bIsComboControl = true;
hWndToUse = ::GetParent(hWnd);
}
else if (mapControls.find(hWnd) != mapControls.end())
hWndToUse = hWnd;
if (hWndToUse == nullptr)
return false;
// Process
UpdateData(TRUE);
// Get the correct selection from the control
DWORD dwSel = (bIsComboControl) ?
((CComboBox*)mapControls[hWndToUse])->GetEditSel() :
((CEdit*)mapControls[hWndToUse])->GetSel();
// Encode the text
CMeetingScheduleAssistantApp::EncodeText(*mapControlsText[hWndToUse],
strCode, LOWORD(dwSel), HIWORD(dwSel));
UpdateData(FALSE);
return true;
}
We have an MFC application that has been used and maintained for many years. Recently we made some administrative changes to some computers that are running the application. Now the software occasionally crashes when printing from the application.
We are using pretty standard MFC code to initiate the printing. We added try/catch blocks around what we felt like are the pertinent areas of the code with no luck. Whatever is failing does not seem to throw.
We get the typical dialog stating that "____ MFC Application has stopped working". Closing the program is the only option.
The windows event logger shows that our application is the Faulting application.
The exception code is 0xc0000005, which appears to be an Access Denied error.
The application is in the CView::OnFilePrint() code when the crash occurs.
We have added some logging, and we know that we get through DoPreparePrinting, and OnBeginPrinting.
We believe that CDC::StartDoc would be the next thing called, then CView::OnPrepareDC. We don't get to OnPrepareDC when we fail.
We don't seem to find the source code for CView::OnFilePrint, so we are not sure what it looks like. From research online, we think that things happen in this order in 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...
}
I would like to have the source for it so we could attempt to rewrite it and try to gracefully fail instead of failing by crashing.
I'm looking for:
1) any suggestions as to how to figure out why the process of printing causes our application to crash.
2) A location for where the CView::OnFilePrint code is located, if available.
(the only idea I have left to narrow down the problem is to call our own version of this so that we can step through it and add logging and/or see if we can at least fail gracefully when it the problem occurs.)
The printer is Xerox Phaser 3610, for what its worth.
source code for CView::OnFilePrint should be in C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\src\mfc\viewprnt.cpp, depending on VS version. There could also be a problem with printer initialization/access.
If there is any error it is most likely due to printer initialization. You can override OnFilePrint and add CPrintInfo printInfo for testing. Example:
//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();
}
As noted in comments, another possibility is that there is a bug somewhere else in the code, which may not necessarily be related to printing.
Inside of CView::OnFilePrint, this occurs:
CWnd * hwndTemp = AfxGetMainWnd();
It turns out that if you don't call OnFilePrint from the main thread, this returns NULL.
Due to slight timing changes when the computers were logged onto a domain, OnFilePrint was being called from another thread. This causes the above call to return null, then when this line gets executed:
hwndTemp->EnableWindow(FALSE);
The application crashes.
There are several ways to fix this. One is to use this:
CWnd * hwndTemp = AfxGetApp()->GetMainWnd();
In place of this:
CWnd * hwndTemp = AfxGetMainWnd();
Another way is to assure that OnFilePrint is only called from the main thread.
A cut to the chase version of the code in CView::OnFilePrint is here:
// 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);
Full version of CView::OnFilePrint is below.
The OnFilePrint code, with the problem area noted:
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
}
}
I have created a dailog box with custom control. I am using ultimate grid in my application. (https://www.codeproject.com/Articles/20183/The-Ultimate-Grid-Home-Page).
I am having an error (Debug Assertion Failed) when I run my project.
BOOL CCustomControlDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
// Add "About..." menu item to system menu.
m_grid.AttachGrid(this, IDC_CUSTOM1);// ERROR LINE
return TRUE; // return TRUE unless you set the focus to a control
}
//Attach grid implementation
BOOL CUGCtrl::AttachGrid(CWnd * wnd,UINT ID){
if( SubclassDlgItem(IDC_CUSTOM1,wnd)) // ERROR LINE
{
long style = GetWindowLong(m_hWnd,GWL_STYLE);
style = style|WS_CLIPCHILDREN|WS_TABSTOP;
SetWindowLong(m_hWnd,GWL_STYLE,style);
// if the parent window is specified
if(wnd!= NULL)
{
LOGFONT logFont;
CFont *pTempFont = wnd->GetFont();
pTempFont->GetLogFont( &logFont );
// ceate a font object based on the font information retrieved from
// parent window. This font will be used as grid's default font.
int nIndex = AddFont( logFont.lfHeight, logFont.lfWidth, logFont.lfEscapement,
logFont.lfOrientation, logFont.lfWeight, logFont.lfItalic,
logFont.lfUnderline, logFont.lfStrikeOut, logFont.lfCharSet,
logFont.lfOutPrecision, logFont.lfClipPrecision,
logFont.lfQuality, logFont.lfPitchAndFamily, logFont.lfFaceName );
SetDefFont( nIndex );
// create a font that will be used for the heading cells. This object
// is almost identical to the grid's default font, except its weight
// was increased by 200.
nIndex = AddFont( logFont.lfHeight, logFont.lfWidth, logFont.lfEscapement,
logFont.lfOrientation, logFont.lfWeight + 200, logFont.lfItalic,
logFont.lfUnderline, logFont.lfStrikeOut, logFont.lfCharSet,
logFont.lfOutPrecision, logFont.lfClipPrecision,
logFont.lfQuality, logFont.lfPitchAndFamily, logFont.lfFaceName );
CUGCell cell;
GetHeadingDefault( &cell );
cell.SetFont( GetFont( nIndex ) );
SetHeadingDefault( &cell );
}
CreateChildWindows();
// When WS_EX_RTLREADING style was specified for the place holder
// window, then set the grid to be in RTL layout mode.
style = GetWindowLong( m_hWnd, GWL_EXSTYLE );
if ( style&WS_EX_RTLREADING )
SetGridLayout( 1 );
OnSetup();
OnSheetSetup(0);
// Allow drawing after the grid is initialized
m_GI->m_paintMode = TRUE;
// Adjust the grid's components to fit current setup
AdjustComponentSizes();
return TRUE;
}
return FALSE;
}
Anyone having any idea how to fix this??
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
void CCustomControlDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CCustomControlDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
DDX_Control(pDX, IDC_CUSTOM1, m_drawpad);
//}}AFX_DATA_MAP
}
The issue is, that you are attaching two different C++ objects to the same control (IDC_CUSTOM1). The entry in DoDataExchange() implicitly performs the subclassing by calling DDX_Control(pDX, IDC_CUSTOM1, m_drawpad);, while the code in OnInitDialog() is more explicit (m_grid.AttachGrid(this, IDC_CUSTOM1);), but essentially does the same thing.
To fix this, you have a number of options, depending on what you are after:
Remove the DDX_Control() call in DoDataExchange() if you don't need the m_drawpad object attached to IDC_CUSTOM1.
Remove the m_grid.AttachGrid() call inside OnInitDialog(), if you don't need the m_grid object attached to IDC_CUSTOM1.
Add an additional control placeholder to your dialog resource and use that for either of those objects, in case you need both.
I recently ported the following code to wx3.0 under visual studio 2013:
void PanelFileControl::on_retrieve_clicked(wxCommandEvent &event)
{
if(!chosen_files.empty())
{
Csi::ModalCounter counter;
wxDirDialog query(
this,
make_wxString(my_strings[strid_choose_retrieve_dir]),
make_wxString(wxGetApp().get_config()->get_last_prog_dir()));
int rcd;
query.CentreOnParent();
rcd = query.ShowModal();
if(rcd == wxID_OK)
{
// we need to generate an operation for every selected file.
StrAsc path(make_StrAsc(query.GetPath()));
DlgFileControl::operations_type operations;
if(path.last() != Csi::FileSystemObject::dir_separator())
path.append(Csi::FileSystemObject::dir_separator());
for(files_type::iterator fi = chosen_files.begin(); fi != chosen_files.end(); ++fi)
{
file_type const &file(*fi);
StrAsc file_path(path + file.get_file_name());
bool use_file(true);
if(Csi::file_exists(file_path.c_str()))
{
OwxStringStream message;
message << boost::format(my_strings[strid_overwrite_file_confirm].c_str()) %
file_path;
wxMessageDialog overwrite_query(
this,
message.str(),
wxT(""),
wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
int rcd;
overwrite_query.CentreOnParent();
rcd = overwrite_query.ShowModal();
if(rcd != wxID_YES)
use_file = false;
}
if(use_file)
operations.push_back(new ReceiveFileOperation(file, file_path));
}
// we can now display the operation dialogue
if(!operations.empty())
{
DlgFileControl dialogue(this, device_name, operations);
dialogue.show_modal();
}
}
}
} // on_retrieve_clicked
Following this change (which didn't require any changes to the code above), I have received complaints that, if the user selects the desktop and then double clicks on a directory on the desktop, that the file save operation fails. This appears to be a result of the path produced by the wxDirDialog::GetPath() and has only been seen under windows vista. I have followed up some testing and I find that, under Vista, the last path component is mentioned twice in the string returned by GetPath().
Has anyone seen this issue? Are there any work arounds?
I found that I can address the issue by preventing the wxDirDialog from using the IFILEDIALOG interface from being used. My ShowModal() method now looks like this:
int wxDirDialog::ShowModal()
{
WX_HOOK_MODAL_DIALOG();
wxWindow* const parent = GetParent();
WXHWND hWndParent = parent ? GetHwndOf(parent) : NULL;
// Use IFileDialog under new enough Windows, it's more user-friendly.
int rc;
#if wxUSE_IFILEDIALOG
if ( wxGetWinVersion() > wxWinVersion_Vista )
{
rc = ShowIFileDialog(hWndParent);
}
else
{
rc = wxID_NONE;
}
if ( rc == wxID_NONE )
#endif // wxUSE_IFILEDIALOG
{
rc = ShowSHBrowseForFolder(hWndParent);
}
// change current working directory if asked so
if ( rc == wxID_OK && HasFlag(wxDD_CHANGE_DIR) )
wxSetWorkingDirectory(m_path);
return rc;
}
Using Visual studio 2010 and MFC Doc/View Applications I want my SDI application to start up completely hidden, and after sometime or with receiving some message from tray icon it shows the mainframe, view and so on. I change the line m_pMainWnd->ShowWindow(SW_NORMAL); to m_pMainWnd->ShowWindow(SW_HIDE); in BOOL CMyApp::InitInstance() but the main frame just flickers after executing the application and then goes hiiden what should I do inorder to avoid this problem and keep the showing capability of main frame when ever I want.
Here is the solution for SDI/MDI app: The new MFC (with VC2010) overrides the m_nCmdShow value with a setting stored in the system registry. To change this behaviour, simply override the LoadWindowPlacement virtual function in the application class.
BOOL CAdVisuoApp::LoadWindowPlacement(CRect& rectNormalPosition, int& nFflags, int& nShowCmd)
{
BOOL b = CWinAppEx::LoadWindowPlacement(rectNormalPosition, nFflags, nShowCmd);
nShowCmd = SW_HIDE;
return b;
}
Normally if you have VC2005 or earlier the following will do:
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
m_nCmdShow = SW_HIDE;
// Dispatch commands specified on the command line. Will return FALSE if
// app was launched with /RegServer, /Register, /Unregserver or /Unregister.
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The one and only window has been initialized, so show and update it
m_pMainWnd->ShowWindow( m_nCmdShow);
m_pMainWnd->UpdateWindow();
Note that m_nCmdShow should be set to SW_HIDE before ProcessShallCommand for the flicker not to occur.
It looks like there might be a bug in VC2010 though. Since I have done this before it intrigued me and tried a fresh VC2010 project but it was not working. I noticed the problem was deep in the following MFC function.
BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
CWnd* pParentWnd, CCreateContext* pContext)
{
// only do this once
ASSERT_VALID_IDR(nIDResource);
ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource);
m_nIDHelp = nIDResource; // ID for help context (+HID_BASE_RESOURCE)
CString strFullString;
if (strFullString.LoadString(nIDResource))
AfxExtractSubString(m_strTitle, strFullString, 0); // first sub-string
VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
// attempt to create the window
LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);
CString strTitle = m_strTitle;
if (!Create(lpszClass, strTitle, dwDefaultStyle, rectDefault,
pParentWnd, ATL_MAKEINTRESOURCE(nIDResource), 0L, pContext))
{
return FALSE; // will self destruct on failure normally
}
// save the default menu handle
ASSERT(m_hWnd != NULL);
m_hMenuDefault = m_dwMenuBarState == AFX_MBS_VISIBLE ? ::GetMenu(m_hWnd) : m_hMenu;
// load accelerator resource
LoadAccelTable(ATL_MAKEINTRESOURCE(nIDResource));
if (pContext == NULL) // send initial update
SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);
return TRUE;
}
m_nCmdShow is still SW_HIDE when this function executes but it changes to SW_SHOWNORMAL when if (!Create(lpszClass... line executes. I don't know why this happens in VC2010 project only, sounds like a bug to me.
My sample project was SDI.
This comes from a dialog based application but you should be able to convert it to a Doc/View app as well. You need to handle the OnWindowPosChanging event. The key line is the the one inside the if statement. This allows my application to start completely hidden from view.
void CIPViewerDlg::OnWindowPosChanging( WINDOWPOS FAR* lpWindowPosition )
{
if( !m_bVisible )
{
lpWindowPosition->flags &= ~SWP_SHOWWINDOW;
}
CDialog::OnWindowPosChanging( lpWindowPosition );
}
Make sure that you are correctly turning off the WS_VISIBLE bit in CMainFrame::PreCreateWindow(CREATESTRUCT& cs). Something like this should worK:
cs.style &= ~WS_VISIBLE;
We had simply been negating the bit instead of turning it off, and we got away with it in VS 6.0 because this function was called only once. It is called twice in newer versions of Visual Studio, so in the second call we were flipping it right back on again. :-O
I tried all for Visual Studio 2010 and finished up with:
class CMainFrame : public CFrameWndEx
{
// ...
// Attributes
public:
BOOL m_bForceHidden;
// ...
// Overrides
public:
virtual void ActivateFrame(int nCmdShow = -1);
//...
};
CMainFrame::CMainFrame() : m_bForceHidden(TRUE)
{
// ...
}
void CMainFrame::ActivateFrame(int nCmdShow)
{
if(m_bForceHidden)
{
nCmdShow = SW_HIDE;
m_bForceHidden = FALSE;
}
CFrameWndEx::ActivateFrame(nCmdShow);
}
Other tricks did not work for me.
Found solution at:
http://forums.codeguru.com/showthread.php?478882-RESOLVED-Can-a-Doc-view-be-hidden-at-startup
I found in VS2017 (using BCGControlBar Pro which is what MFC Feature Pack was based on) that you have to handle things in two places:
BOOL CMainFrame::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)
{
if (!__super::LoadFrame(nIDResource, dwDefaultStyle, pParentWnd, pContext))
{
return FALSE;
}
// undo what __super::LoadFrame() does where it will set it to SW_NORMAL if not SW_MAXIMIZED
AfxGetApp()->m_nCmdShow = SW_HIDE;
}
BOOL CTheApp::LoadWindowPlacement(CRect& rectNormalPosition, int& nFflags, int& nShowCmd)
{
BOOL b = __super::LoadWindowPlacement(rectNormalPosition, nFflags, nShowCmd);
nShowCmd = SW_HIDE;
return b;
}