Problem using TVN_SELCHANGED seems to go in a continuous cycle - visual-c++

I have this event handler in a modelless popup dialog tree control:
void CAssignHistoryDlg::OnTvnSelchangedTreeHistory(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
if (!m_bBuildTreeMode)
{
if ((pNMTreeView->itemOld.hItem == nullptr && !m_bFirstSelChangeEvent) ||
pNMTreeView->itemOld.hItem != nullptr)
{
m_bFirstSelChangeEvent = true;
if (m_treeHistory.GetParentItem(pNMTreeView->itemNew.hItem) == nullptr)
{
// We must update the correct combo
// and associated string (in the SERVMEET_S structure)
if (m_pCombo != nullptr && m_pStrText != nullptr)
{
CString strExtractedName = ExtractName(pNMTreeView->itemNew.hItem);
m_pCombo->SetWindowText(strExtractedName);
*m_pStrText = strExtractedName;
}
GetParent()->PostMessage(UM_SM_EDITOR_SET_MODIFIED, (WPARAM)TRUE);
}
}
}
*pResult = 0;
}
What I don't understand is why once this event is triggered is that it goes in a continuous cycle.
Does anything jump out at you as wrong?

I don't know why the message seemed to be going in a continuous cycle. Maybe it was because I was inserting breakpoints or adding temporary popup message boxes to debug. Either way, I worked out the minor adjustment I needed:
void CAssignHistoryDlg::OnTvnSelchangedTreeHistory(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
if (!m_bBuildTreeMode)
{
if ((pNMTreeView->itemOld.hItem == nullptr && !m_bFirstSelChangeEvent) ||
pNMTreeView->itemOld.hItem != nullptr)
{
m_bFirstSelChangeEvent = true;
if (m_treeHistory.GetParentItem(pNMTreeView->itemNew.hItem) == nullptr)
{
// We must update the correct combo
// and associated string (in the SERVMEET_S structure)
if (m_pCombo != nullptr && m_pStrText != nullptr)
{
CString strExtractedName = ExtractName(pNMTreeView->itemNew.hItem);
m_pCombo->SetWindowText(strExtractedName);
// Bug fix - Only set as modified if the name is different
if(*m_pStrText != strExtractedName)
GetParent()->PostMessage(UM_SM_EDITOR_SET_MODIFIED, (WPARAM)TRUE);
*m_pStrText = strExtractedName;
}
}
}
}
*pResult = 0;
}
As you can see, I have changed how and where I post the UM_SM_EDITOR_SET_MODIFIED message. This causes my application to work correctly. Previously it was always setting it as modified (multiple times). So even if you had just saved the file, it then was marked as modified again. This problem no longer happens.

Related

CView::OnFilePrint crashing MFC application

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

CTreeCtrl: How to clear the focus of selected item

Am new to MFC, I want to replicate the exact Ctrl+Page Down and Ctrl+Page Up behavior to regular Page Down/Page Up keys without any supporting keys (Ctrl/Shift). I have been trying to clear the focus of item which is getting selected automatically on striking the keys Page Up and Page Down.
I've tried with this code but its not working:
case VK_NEXT: // pagedown
case VK_PRIOR: // pageup
lhItem = GetFocusedItem();
if (IsSelected(lhItem))
{
CTreeCtrl::SetItemState(lhItem, 0, TVIS_SELECTED);
}
break;
Can anyone please help me in solving it
The Code need to written in OnSelChanging & OnSelChanged Event Handler functions
void CTreeCtrl::OnSelchanging(NMHDR *pNMHDR, LRESULT *pResult)
{
HTREEITEM hNew = pNMTreeView->itemNew.hItem;
HTREEITEM hOld = pNMTreeView->itemOld.hItem;
m_bOldItemSelected = hOld && (CTreeCtrl::GetItemState(hOld, UINT(TVIS_SELECTED)) & TVIS_SELECTED);
if (GetSelectedCount() > 1)
{
if (m_bPgUpState || m_bPgDownState)
{
//Check the state of New Item
if ((pNMTreeView->itemNew.state & TVIS_SELECTED))
{
// If the item is selected, so make sure OnSelchanged()
// will "select" it !
m_bNewItemSelected = TRUE;
}
else if (!(pNMTreeView->itemNew.state & TVIS_SELECTED))
{
// The New item is not selected, so make sure OnSelchanged()
// will not "re-select" it !
m_bNewItemSelected = FALSE;
CTreeCtrl::SetItemState(hNew, UINT(~TVIS_SELECTED), UINT(TVIS_SELECTED));
}
}
}
void TreeCtrl::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
HTREEITEM itemNew = pNMTreeView->itemNew.hItem;
HTREEITEM itemOld = pNMTreeView->itemOld.hItem;
if ((m_bPgUpState || m_bPgDownState) && (GetSelectedCount() > 1)
&& (pNMTreeView->itemOld.hItem != NULL || pNMTreeView->itemNew.hItem != NULL))
{
// It had the focus so Keep selection at old item
if (itemOld && m_bOldItemSelected)
{
CTreeCtrl::SetItemState(itemOld, UINT(TVIS_SELECTED), UINT(TVIS_SELECTED));
m_bOldItemSelected = FALSE;
}
else
{
// Do-not select the item if it is not selected
CTreeCtrl::SetItemState(itemOld, UINT(~TVIS_SELECTED), UINT(TVIS_SELECTED));
}
}
In this article you'll find solution for every thing about CTreeCtrl
Full-Featured Tree Control

Accidently deleting entire linked list when trying to delete the head

I'm working on a checker's simulation game for my C++ class. My issue is with the linked list that holds the checkers. I can delete any checker perfectly with the exception of the head of the list. I've looked around here and other websites and I believe there's a memory leak somewhere. I'm fairly new to C++ so I'm not sure what to really do other than playing around with things (which will probably just create a bigger problem). I've never posted here before, so excuse me if the formatting is slightly off or too messy. I'll try to make it brief. First, here's a snippet of the node class for the linked list.
class CheckerpieceNode
{
private:
Checkerpiece *Node;
CheckerpieceNode *Next;
public:
CheckerpieceNode(); // sets Node and Next to NULL in .cpp file
void setNode(Checkerpiece *node);
void setNext(CheckerpieceNode *next);
Checkerpiece* getNode();
CheckerpieceNode* getNext();
};
And the functions are set up pretty much as you would expect in a Checkerpiece.cpp class.
Here's how the code is used. Its called by a Checkerboard object in my main class.
theCheckerboard.removeChecker(theCheckerboard.findChecker(selector->getCurrentX() + 0, selector->getCurrentY() - VERTICAL_SHIFT, listHead), listHead);
The VERTICAL_SHIFT simply has to do with the way my checkerboard graphic is on the console. Since it works perfectly for all other nodes (excluding the head) I've ruled it out as a source of error. Selector is a checkerpiece object but its not part of the list.
Here's the actual findChecker and removeChecker code from Checkerboard class.
Checkerpiece* findChecker(int x, int y, CheckerpieceNode* list_head)
{
if(list_head== NULL) return NULL; // do nothing
else
{
CheckerpieceNode* node = new CheckerpieceNode;
node = list_head;
while(node != NULL && node->getNode() != NULL)
{
if()// comparison check here, but removed for space
{
return node->getNode();
delete node;
node = NULL;
}
else // traversing
node = node->getNext();
}
return NULL;
}
}
void removeChecker(Checkerpiece* d_checker, CheckerpieceNode* list_head)
{
if(list_head== NULL) // throw exception
else
{
CheckerpieceNode *temp = NULL, *previous = NULL;
Checkerpiece* c_checker= new Checkerpiece;
temp = list_head;
while(temp != NULL && temp->getNode() != NULL)
{
c_checker= temp->getNode();
if(d_checker!= c_checker)
{
previous = temp;
temp = temp->getNext();
}
else
{
if(temp != list_head)
{
previous->setNext(temp->getNext());
delete temp;
temp = NULL;
}
else if(temp == list_head) // this is where head should get deleted
{
temp = list_head;
list_head= list_head->getNext();
delete temp;
temp = NULL;
}
return;
}
}
}
}
Oh my, you're complicating it. Lots of redundant checks, assignments and unnecessary variables (like c_checker which leaks memory too).
// Write down the various scenarios you can expect first:
// (a) null inputs
// (b) can't find d_checker
// (c) d_checker is in head
// (d) d_checker is elsewhere in the list
void removeChecker(Checkerpiece* d_checker, CheckerpieceNode* list_head) {
// first sanitize your inputs
if (d_checker == nullptr || list_head == nullptr) // use nullptr instead of NULL. its a keyword literal of type nullptr_t
throw exception;
// You understand that there is a special case for deleting head. Good.
// Just take care of it once and for all so that you don't check every time in the loop.
CheckerpieceNode *curr = list_head;
// take care of deleting head before traversal
if (d_checker == curr->getNode()) {
list_head = list_head->next; // update list head
delete curr; // delete previous head
return; // we're done
}
CheckerpieceNode *prev = curr;
curr = curr->next;
// traverse through the list - keep track of previous
while (curr != nullptr) {
if (d_checker == curr->getNode()) {
prev->next = curr->next;
delete curr;
break; // we're done!
}
prev = curr;
curr = curr->next;
}
}
I hope that helps. Take the time to break down the problem into smaller pieces, figure out the scenarios possible, how you'll handle them and only then start writing code.
Based on this edit by the question author, the solution he used was to:
I modified the code to show the address passing in the checker delete
function.
void delete_checker(Checker* d_checker, CheckerNode* &list_head) // pass by address
{
if(list_head== NULL) // throw exception
else
{
CheckerNode*temp = NULL, *previous = NULL;
Checker* c_checker= new Checker;
temp = list_head;
while(temp != NULL && temp->node!= NULL)
{
c_checker= temp->node;
if(d_checker!= c_checker)
{
previous = temp;
temp = temp->next;
}
else
{
if(temp != list_head)
{
previous->next = temp->next;
delete temp;
temp = NULL;
}
else if(temp == list_head) // this is where head should get deleted
{
temp = list_head;
list_head= list_head->next;
delete temp;
temp = NULL;
}
delete c_checker;
c_checker = nullptr;
return;
}
}
}
}
removeChecker cannot modify the value of list_head as it is past by value. The method signature should be:
void removeChecker(Checkerpiece* d_checker, CheckerpieceNode** list_head)
// You will need to call this function with &list_head
or
void removeChecker(Checkerpiece* d_checker, CheckerpieceNode* &list_head)
// Calling code does not need to change

Comparing pointers fails mystically in VC++

I have a tree structure and I want to find all nodes matching a given criteria. Each time I call the find function, it returns next matching node. Children are searched by recursive function call.
For some reason a key comparison of pointers fails for this implementation. Please see the code below, I have pointed out the failing comparison.
HtmlTag* HtmlContent::FindTag(string tagName, string tagParameterContent)
{
if (tagName.empty() && tagParameterContent.empty())
return NULL;
if (this->startTag == NULL)
return NULL;
this->findContinue = this->FindChildren(this->startTag, &tagName, &tagParameterContent);
return this->findContinue;
}
HtmlTag* HtmlContent::FindChildren(HtmlTag* firstTag, string* tagName, string* tagParameterContent)
{
HtmlTag* currentTag = firstTag;
HtmlTag* childrenFound = NULL;
while (currentTag != NULL)
{
if (!tagName->empty() && *tagName == currentTag->tagName)
{
if (tagParameterContent->empty() || currentTag->tagParameters.find(*tagParameterContent, 0) != -1)
{
if (this->findContinue == NULL)
break; // break now when found
else if (this->findContinue == currentTag) // TODO why this fails?
this->findContinue == NULL; // break on next find
}
}
if (currentTag->pFirstChild != NULL)
{
childrenFound = this->FindChildren(currentTag->pFirstChild, tagName, tagParameterContent);
if (childrenFound != NULL)
{
currentTag = childrenFound;
break;
}
}
currentTag = currentTag->pNextSibling;
}
return currentTag;
}
VC++ compiler accepts this code but for some reason I can't put a breakpoint on this comparison. I guess this is optimized out, but why? Why this comparison fails?
I think that you shoud replace == with = in assignment after comparison. Compiler optimalized this whole section because it doesnt do anything useful.

Detect when dialog sets focus to various combo controls using PreTranslateMessage

I have a CDialog and I am trying to override PreTranslateMessage like this:
BOOL CWeekendMeetingDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_SETFOCUS)
{
if (::GetDlgCtrlID(pMsg->hwnd) != IDC_COMBO_PT_CHAIRMAN &&
::GetDlgCtrlID(pMsg->hwnd) != IDC_COMBO_PT_OPEN_PRAYER &&
::GetDlgCtrlID(pMsg->hwnd) != IDC_COMBO_WT_CLOSE_PRAYER &&
::GetDlgCtrlID(pMsg->hwnd) != IDC_COMBO_WEEKEND_HOST &&
::GetDlgCtrlID(pMsg->hwnd) != IDC_COMBO_WEEKEND_COHOST &&
::GetDlgCtrlID(pMsg->hwnd) != IDC_COMBO_PT_READER)
{
if (m_gridAssignHist.GetRowCount() != 1)
{
m_gridAssignHist.SetRowCount(1);
}
}
}
return CDialog::PreTranslateMessage(pMsg);
}
It is not working. What I want to do is reset my CGridCtrl row count to 1 when other controls gain focus (eg: edit combo). But if I put a breakpoint inside the first if it is never intercepted.
At the moment the only thing I can think of is to renumber all of the combo IDs on the dialog so they are sequential and then use a command range for OnSetFocus and detect in that handler. But there are also some CEdit controls.
Can't I use PTM though and avoid that? For me it seems it would be the easiest.
Here is the dialog:
At the moment I have 7 combo OnSetFocus handlers. When they fire the display specific assignment history in the grid on the right.
So if the user moves to any other control on the dialog, the assignment history control will not apply. And that was why i wanted to reset the history to just the header row. And I was hoping to do this with PTM.
Child control notifications are received in the parent dialog via WM_COMMAND messages. Overriding MFC dialog's OnCommand catches those notifications (CBN_SETFOCUS, EN_SETFOCUS etc).
void DoSomething()
{ /* ... */ }
BOOL CWeekendMeetingDlg::OnCommand(WPARAM wParam, LPARAM /*unused*/)
{
switch (HIWORD(wParam))
{
case CBN_SETFOCUS:
switch (LOWORD(wParam))
{
case IDC_COMBO_PT_CHAIRMAN:
// ...more combobox IDCs
DoSomething();
break; // or return TRUE to bypass any message map handlers
}
break;
case EN_SETFOCUS:
switch (LOWORD(wParam))
{
case IDC_EDIT_WHATEVER:
// ...more editbox IDCs
DoSomething();
break;
}
break;
// ...more notification codes
}
return CDialog::OnCommand(wParam, lParam);
}
I have added a 2 second timer to the dialog:
void CWeekendMeetingDlg::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == 1)
{
CWnd* pControl = GetFocus();
if (pControl != nullptr)
{
CWnd* pParent = pControl->GetParent();
if (pParent != nullptr)
{
int iCtrlID = pParent->GetDlgCtrlID();
if (iCtrlID != IDC_COMBO_PT_CHAIRMAN &&
iCtrlID != IDC_COMBO_PT_OPEN_PRAYER &&
iCtrlID != IDC_COMBO_WT_CLOSE_PRAYER &&
iCtrlID != IDC_COMBO_WEEKEND_HOST &&
iCtrlID != IDC_COMBO_WEEKEND_COHOST &&
iCtrlID != IDC_COMBO_PT_READER)
{
if (m_gridAssignHist.GetRowCount() != 1)
{
m_gridAssignHist.SetRowCount(1);
UpdateData(TRUE);
m_strAssignHistLabel = _T("Assignment:");
UpdateData(FALSE);
}
}
}
}
}
CDialog::OnTimer(nIDEvent);
}
This seems to work fine.

Resources