How can I "validate" CWnd* object? - c++

(There's a TL;DR on the last line)
I'm implementing a handler to close selected windows open in a software application. Here's a rough code:
void CDlg_Dummy_Dialog::OnCloseWindows()
{
for (int i = 0; i < m_WindowsInfo.size(); i++) {
Window_Node *pWN = &m_WindowsInfo.at(i);
if (pWN->checked && IsWindow(pWN->pWnd->GetSafeHwnd())) {
pWN->pWnd->GetParentFrame()->SendMessage(WM_CLOSE);
}
}
}
Here are some declarations of the parameters shown above:
struct Window_Node {
CString name;
CString path;
CWnd *pWnd;
BOOL checked;
HICON icon;
....
};
class CDlg_Dummy_Dialog : public CDialog {
...
protected:
std::vector<struct Window_Node> m_WindowsInfo;
...
}
Also, there can be multiple instances of Window_Node with different pWnd parameter, originating from a single CDocument class (ie. different types of windows exist to show different displays for the document).
For this software, if the first window of the document (which is always the "green" Window type in the diagram) is closed, all other windows associated with that document will automatically be closed with them. This is where the problem happens.
If the user selects multiple windows from the same document (with the green window among them), it closes all windows by the time it finishes the first iteration, and all pWnd pointers are now pointing to a now unassigned memory. Therefore, when it tries to call GetSafeHwnd() on the next iteration, it prompts a memorry access violation error:
First-chance exception at 0x00000000521B4AD0 (mfc100d.dll) in Settle3D.exe: 0xC0000005: Access violation reading location 0x00000000136943E0.
Unhandled exception at 0x00000000521B4AD0 (mfc100d.dll) in Settle3D.exe: 0xC000041D: An unhandled exception was encountered during a user callback.
I'm aware that the easy fix would be to iterate through the vector in the opposite direction. However, I am trying to integrate this method on several other software as well, and they don't necessarily organize their windows in the same manner.
So, after all the long question above, here's TL;DR:
Is there any way to check if an instance of CWnd* is pointing to a valid window?

One possibility would be to start with your main window, and recursively search through the child windows if you find the HWND in question.
Get the first child with CWnd::GetWindow(GW_CHILD) and the next windows with CWnd::GetWindow(GW_HWNDNEXT).

Related

MFC "memory allocation violation"

I created a check box in a dialog box and trying to access its condition whether checked or not. This is my code:
CButton *m_ctlCheckBlack = (CButton *)GetDlgItem(IDC_BLACK);
int chkBoxBlack = m_ctlCheckBlack->GetCheck();
As I run through this code, it pops up an exception saying :
Exception thrown at 0x0FA45564 (mfc140d.dll) in braille_obr.exe: 0xC0000005:
Access violation reading location 0x00000020.
any help will be appreciated. thank you.
There is rarely a good reason to use GetDlgItem. In your resource editor, right click the button and 'Add Variable..'
It will default as a control. Give it a name. You will get a member in the dialog class:
CButton myButtonName;
Now it should be safe to:
myButtonName.GetChecked( );
as it will have been created and properly subclassed.
Perhaps, I doubt it tried to get the state of button when the Button was not yet constructed.
int chkBoxBlack = m_ctlCheckBlack != NULL ? m_ctlCheckBlack->GetCheck() : 0;
Or, do you try to operate from outside dialog?
If it is so, you had better try to do this.
■CButton *m_ctlCheckBlack = (CButton *)yourdlg.GetDlgItem(IDC_BLACK);
■FindWindowEx(yourdlg.GetSafeHwnd(), NULL, NULL, "(your button caption)");

Legacy MFC cross-thread exception in CSimpleString

I have an old legecy ATL/MFC application with two threads, the main Window-Thread and a Render-Thread. My problem is I have random, access-violation errors related to a CSimpleString; i.e. access violation, 0xdddddddd etc...
I have deduced the problem is the two threads accessing the same string at the same time, one trying to use it to render (the MFC main Window-Thread) and one trying to update the string (the Render-Thread).
From the MFC side; the class is
class CDisplay : public CStatic
{
public:
CString m_strDisplay;
...
void SetDisplay(CString str, int nMode = -1);
...
}
There is no paint override and the text is basically rendered via CStatic.
Now, the SetDisplay method is what is called from the Render-Thread; and it prodominent code is:
if (m_strDisplay != str)
{
m_strDisplay = str;
SetWindowText(str + " ");
}
My problem here, is that I need a critical section; but I don't know how to get the MFC side to adhere to it.
Anyone have some wisdom in making MFC thread-safe and avoiding these problems?
Make GUI updates (SetWindowText) in the MFC main thread only. In the render thread, set a variable (protected by critical section) and/or send a message, and then perform the actual GUI element manipulation in the MFC main thread.

Getting ActiveX window handle

I have followed this link to get the window handle of a ActiveX control
Sample Code from microsoft's site
// The following code should return the actual parent window of the ActiveX control.
HWND CMyOleControl::GetActualParent()
{
HWND hwndParent = 0;
// Get the window associated with the in-place site object,
// which is connected to this ActiveX control.
if (m_pInPlaceSite != NULL)
m_pInPlaceSite->GetWindow(&hwndParent);
return hwndParent; // Return the in-place site window handle.
}
But in my case I keep finding that "m_pInPlaceSite" is always NULL. I'm trying to run this code in my controls FinalConstruct. Is there something else I need to implement for the m_pInPlaceSite to be given a value? Or do I need to Query to get the value.
Thanks
FinalConstruct is way too early. In FinalConstruct your class is just being created and is not yet initialized. There is no "in place" site, there is no yet site at all.
Your control will be called by its owner, it will be given its site, then activated - only then you will possibly have m_pInPlaceSite available.

Share Data Between One Member Variable and Several Controls w/DoDataExchange /Force Update Controls on All Windows

I have a VS 2005 C++ MFC project that contains several CDialog based windows. The program communicates with a custom piece of hardware and displays different readings on the different windows. The data is read from the hardware using a public static member function named ReadHardWare() of a class named CTools. ReadHardWare() places the data read from the hardware in an array with global scope and each window has the ability to read from this array. On each window I have an indicator that lights up showing that a read from hardware operation is in progress. This indicator is a National Instruments
Measurement Studio CNiButton class control. CTools has a public static member variable of the CNiButton type named m_read_in_progress_status that I use to identify if a read operation is in progress (m_read_in_progress_status.Value == 1) or not (m_read_in_progress_status.Value == 0). My goal is to have all the read indicators on all the opened windows show a read is in or not in progress at the same time. I regularly use DoDataExchange to connect variables with controls but always on a one-to-one basis. In this case I want several controls to connect with a single variable and to automatically update with that variable.
I seem to have two problems:
1) It seems that I am only able to have one control connect with the variable at the same time. Apparently the only control that the variable shares data with is the first one instantiated (i.e. first window opened)
2) If I send a read command from the second window opened, not only does the read indicator on the second window still not show the read status but neither does the read indicator on the first anymore. The only time any of the read indicators work is when I perform a read command from the first window opened. The first window does not seem to want to update its controls when the second window is sending the read command.
So my questions are:
1) How can I allow two controls on two different windows to share the same member variable and update immediately when that member variable's value is changed?
2) How can I make the ReadHardWare() member function (or some other event) force the first window to update its controls even though it is not the window actively calling that function?
In the current case the first window is the parent of the second. However this will not always be the case.
Below is an excerpt of my code.
//Globalvariables.cpp
NI::CNiButton CTools::m_read_in_progress_status;
//Tools.h
class CTools
{
public:
static ReadHardWare();
public:
static NI::CNiButton m_read_in_progress_status;
}
//Tools.cpp
CTools::ReadHardWare()
{
//Declare and initialize variables and other setup code is here
m_read_in_progress_status.Value == 1 //Set read in progress flag high
//Read operations here
m_read_in_progress_status.Value == 0 //Reset read in progress flag low
}
//FirstWindow.cpp
#include "Tools.h"
#include "SecondWindow.h"
void CFirstWindow::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_First_Window_Read_in_Progress_Indicator, CTools::m_read_in_progress_status);
}
//SecondWindow.cpp
#include "Tools.h"
#include "FirstWindow.h"
void CSecondWindow::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_Second_Window_Read_in_Progress_Indicator, CTools::m_read_in_progress_status);
}
DDX_Control establishes an association between control identifier in dialog and particular instance of CWnd based object. I am not sure it's a good idea is to share the same object representing a button for two windows. I suggest to sligthly modify the design. You should create a timer for pooling read operation state. If state is changed you will update the button.
class CTools
{
public:
static void ReadHardWare();
public:
static int m_read_in_progress_status;
}
class CFirstWindow: CDialog
{
public:
void OnTimer(UINT nIDEvent)
{
if(m_read_in_progress_status != CTools::m_read_in_progress_status)
{
m_read_in_progress_status = CTools::m_read_in_progress_status;
m_button.SetCheck(m_read_in_progress_status);
}
}
private:
int m_read_in_progress_status; // Cached status
NI::CNiButton m_button;
};

CDockingManager GetPaneList() causes assertion failure in wincore.cpp?

So I thought this would be pretty simple, but I forgot it's MFC. Instead of registering a notification listener for data model changes that would possibly require a GUI update on each individual control I figure why not register it once and then send a message to all the open dock panes and allow them to update their controls as needed on their own terms for efficiency.
My callback function for handling the notification from the server looks something like this:
void CMainFrame::ChangeCallback(uint32_t nNewVersion, const std::vector<uint32_t>& anChangedObjectTypes)
{
CObList panes;
GetDockingManager()->GetPaneList(panes); // assert failure
if (!panes.IsEmpty())
{
POSITION pos = panes.GetHeadPosition();
while (pos)
{
CDockablePane* pPane = dynamic_cast<CDockablePane*>(panes.GetNext(pos));
if (pPane)
pPane->PostMessage(DM_REFRESH, nNewVersion);
}
}
}
The error I am getting is an assertion failure on line 926 of wincore.cpp
CHandleMap* pMap = afxMapHWND();
ASSERT(pMap != NULL); // right here
There is a comment below this saying this can happen if you pass controls across threads however this is a single threaded MFC application and this is all being done from the main frame.
Does anyone know what else can cause this?
If there is another way to go about sending a message to all the open CDockablePane derived windows in MFC that works as well ...
Here's the obvious workaround that I didn't want to have to do but after hours of debugging and no response here I guess this is a viable answer:
I added std::vector<CDockPane*> m_dockList; to the members of CMainFrame
Now after each call to AddPane in various places that can create and open new dock panes I make a subsequent call to push_back and then I override CDockablePane::OnClose like so:
CMainFrame* pMainFrame = reinterpret_cast<CMainFrame*>(AfxGetMainWnd());
if (pMainFrame)
{
std::vector<CDockPane*>::const_iterator found(
std::find(pMainFrame->DockList()->begin(), pMainFrame->DockList()->end(), this));
if (found != pMainFrame->DockList()->end())
pMainFrame->DockList()->erase(found);
}
CDockablePane::OnClose();
Now this list will only contain pointers to open dock panes which allows me to handle the event notification in my callback and simply do a for loop and PostMessage to each.