Legacy MFC cross-thread exception in CSimpleString - c++

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.

Related

Unify MFC messages and Qt signals-slots/events

I've got an assignment to create sort of a multi-platform C++ GUI library. It wraps different GUI frameworks on different platforms. The library itself provides an interface via which the user communicates uniformly (using the same code) regardless of the platform he's using - typically it's MFC on Windows and Qt elsewhere.
Currently, I'm stuck on messages/events. So far, I've been using MFC messages and Qt signals to get notified of the user's interaction and had no problems with that. The user creates a list box (which contains p_lb - pointer subclassed QListWidget on Qt or CListBox on MFC) and then he ties his own DoSomething() handler with an event.
User's code:
ListBox lb;
lb.AddDoubleClickHandler([](int i) { DoSomething(i); });
This is what it looks like in the library:
// p_lb represents platform list box - QListWidget/CListBox
// handler's int parameter represents the index of the item, that has been double-clicked
void ListBox::AddDoubleClickHandler(const std::function<void(int)>& handler) {
#ifdef _MFC_PLATFORM
// only pushes the handler into the internal list
p_lb->double_click_handlers_.push_back(handler);
#elif _QT_PLATFORM
p_lb->connect(p_lb, &QListWidget::itemDoubleClicked, p_lb,
[handler, p_lb](QListWidgetItem* item) { handler(p_lb->row(item)); });
#endif
}
In MFC I need to call the handlers stored in the list manually:
#ifdef _MFC_PLATFORM
class MyMFCListBox : public CListBox {
public:
std::list<std::function<void(int)>> double_click_handlers_;
afx_msg void OnItemDoubleClick() {
for (const auto& h : double_click_handlers_) {
h(GetCaretIndex()); // call user's stored handler when the event occurs
}
}
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(MyMFCListBox, CListBox)
ON_CONTROL_REFLECT(LBN_DBLCLK, &MyMFCListBox::OnItemDoubleClick)
END_MESSAGE_MAP()
#endif // _MFC_PLATFORM
As I said, this works fine, but now I need to be able to notify UI control about some external event manually (and asynchronously) - using something like MFC's PostMessage. The use case would be something like updating progress bar based on some external computing (which is not necessarily done using this library). E.g. I'm uploading some photos, after each one of them I send a message to the ProgressBar about the current number of uploaded photos and number of all the photos, but I return immediately without waiting for the handler to execute. Also, there needs to be a way of adding new custom events by the user. I know that Qt has a similar method - postEvent, but is this the right way to implement it? Or should I implement my own signal-slot system? I cannot imagine how this would work. Any suggestions are appreciated.

How can I "validate" CWnd* object?

(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).

c++ multithreaded windows GUI (accessing the forms)

I've written a simple console application using windows sockets to work as a proxy between a server and a client. I decided to make a graphical interface for watching all the in/outgoing packets real time (like a very simple wireshark).
The connection between the server and the client runs on a different thread than the message loop. When the server/client sends a packet, I would like that to be displayed (for example added to a simple text control) immediately.
But since I can not access the forms from other thread than the thread where the message loop is I dont know how to handle this.
I've found several solutions in:
- Managed c++
- C++/CLI
- C#,
but not any without using .NET platform. (I really new to this GUI topic so I am not even sure you can use windows forms without .NET)
Maybe QT + C++ could handle this problem, but any other solution than that? If not is it possible to write a wrapper in C# / Java for the native C++ code?
There must be many applications written in C/C++ which using a GUI, what is the general way to do that?
You are absolutely correct that you cannot access a window in a different thread. The proper way to handle this to post a message using the ::PostMessage Win32 API command (or, if you are using a wrapper library around Win32, whatever function in that wrapper that eventually calls PostMessage). Here's a useful link from Microsoft regarding message queues:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644928(v=vs.85).aspx
There is an alternative one, free and open-source, called Nana C++ Library (http://stdex.sourceforge.net), a pure C++ GUI library.
By using Nana library, the multithreading issue can be fixed easily. There is an article on the multithreading in GUI, it would be a choice for your hobby project.
A quick and dirty Win32 solution would involve a critical section, a text buffer, and a timer in the UI thread.
Define a few globals...
CRITICAL_SECTION bufferLock; // critical section (to be initialized somewhere)
char dataBuffer[65536]; // contains the data that will be sent to the form
int newdata = 0; // how much data we got (this variable must be atomic, int is ok)
char uiDataBuffer[65536]; // data available to the form
int overflow = 0; // just in case...
UI thread timer
void onTimer ()
{
if (overflow)
{
// handle this
}
else
if (newdata) // new data to display
{
// take the lock, copy the data and release the lock quickly
EnterCriticalSection(&bufferLock);
int dataread = newdata;
memcpy(uiDataBuffer, dataBuffer, dataread);
newdata = 0;
LeaveCriticalSection(&bufferLock);
// TODO: append the text in uiDataBuffer[] to your text control
}
}
To be called from the worker thread:
void sendData (char* data, int size)
{
EnterCriticalSection (&bufferLock);
if(size+newdata > 65536)
overflow = 1;
else
{
memcpy(dataBuffer+newdata, data, size);
newdata += size;
}
LeaveCriticalSection (&bufferLock);
}
Code untested. Buffer size and timer frequency are to be adjusted.
It is possible to avoid polling the buffer with the timer by using PostMessage() (with a custom message) to signal the UI that new data is available.
If performance is an issue, data exchange between a producer and a consumer thread can also be performed very efficiently with a lock-free FIFO queue.
PostMessage() alone is not a solution to exchange data between threads.

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.

How to write simple background thread in CWorkerThread

I'm trying to asynchronously run function in my add-on for Internet Explorer (I'm writing BHO in VC++). As suggested here I'm trying to use CWorkerThread.
I've been trying to figure it out for hours but still have no idea how to do it. I don't have much experience in ATL. The lack of a good documentations or tutorials on Internet is killing me.
I'm creating class by Add->Class and choosing ATL Simple Object (that's how you add classed to ATL project right?). But how to implement this IWorkerThreadClient? I thought that choosing Add->Implement Interface in Class View would be good but there is no IWorkerThreadClient on the list.
I think I don't know ATL or COM enaugh but can't find good resource for learning this (esspessialy newest ATL7).
I even tried winapi CreateThread approach but it isn't working. I'm passing this class pointer to run static method but something is corrupting with memory later. Nevertheless if It had worked I still would rather use something else than CreateThread.
Right now I have something like this. In OnDocumentComplete there's RemoveImages(sptmlDoc) and I just want to run it asynchronously.
EDIT: What I did with CreateThread:
I tried running RemoveImages function (from here) asynchronously. I created static function in my class with signature like here. RemoveImages has parameter so I copied it to a member of a class:
if (htmlDoc2 != NULL)
{
m_tmpHtmlDocument2 = htmlDoc2;
m_hThread = CreateThread( NULL, 0, MyThreadFunction, this, 0, &m_threadId);
}
and MyThreadFunction:
static DWORD WINAPI MyThreadFunction( LPVOID lpParam )
{
CHelloWorldBHO* myClass = (CHelloWorldBHO*)lpParam;
myClass->RemoveImages(myClass->m_tmpHtmlDocument2);
return 0;
}
I get "Unhandled exception at 0x60c0da05 in iexplore.exe: 0xC0000005: Access violation reading location 0x000001b8." here in the bold line:
void CHelloWorldBHO::DontDisplayElement(CComPtr htmlElement)
{
CComPtr style;
HRESULT hr = htmlElement->get_style(&style);
if (hr == S_OK && style != NULL)
{
static const CComBSTR strNone(L"none");
style->put_display(strNone);
}
}
Your performing a naughty by trying to use a COM handle allocated in 1 thread in another. BHO environment is STA (Single Threaded Apartment) so you should be marshalling the m_tmpHtmlDocument2 object for use in your thread.
Experiance has shown that in some cases IE may let you get away with passing the Browser com object from 1 thread to another and then getting the document and elements afterwards may work. This is entirely unreliable.
Depending on IE 6/7/8 you will have different target threads to execute your actions on, thinking at the levels of per security level/frame/tab/window. basically any time IE creates a new 'Site'
Also to prevent your app from holding the pages active even after navigation away from the page, in FireFox you would use an nsWeakPointer<> , I've never found the equivelant in IE.
Suggestion: Perhaps instead of marshalling com to another thread because your interaction with the page is slow, trying to improve the way you interact with the page and improve performance in process might be a better aim.
Here is an outline using the CThreadPool which will queue up requests, and then execute them when the pool has space.
I use pvWorkerParam to tie the threads back to the site.
I have different types of ActionRequests, you could of course simplify and just pass null for the request.
Note: This doesn't resolve marshalling issues you already have
class ActionRequest{
DontDisplayElement();// define your do stuff in here
};
class ScriptWorker
{
public:
ScriptWorker(void);
virtual ~ScriptWorker(void);
public:
BOOL Initialize(void* pvWorkerParam);
void Execute(ActionRequest *request, void* pvWorkerParam, OVERLAPPED* pOverlapped){
try{
std::auto_ptr<ActionRequest> cleanupRequest(request);
request.DontDisplayElement();
} catch(...) {}
}
void Terminate(void* pvWorkerParam);
private:
boolean m_bCoUninit;
};
Site{
CThreadPool<ScriptWorker> m_scriptWorkerThread;
Site() {
void *pvWorkerParam = this;// or whatever you want to have passed to every script worker and execute in the pool.
m_scriptWorkerThread.Initialize( pvWorkerParam, 1 );
}
OnDocumentComplete() {
m_scriptWorkerThread.QueueRequest( new ActionRequest() );
}
}
and sptmlDoc - is it an IHTMLDocumet* ?
IWorkerThreadClient - never heard of it
"I even tried winapi CreateThread approach but it isn't working. I'm passing this class pointer to run static method but something is corrupting with memomory later"
Keeping it simple is the best design pattern of them all. So stick with CreateThread unless you have good reasons not to. Now, my guess is that the crash occurs because of sptmlDoc being passed to the thread for later processing. The thing is such pointers are only valid from the BeforeNavigate event until DocumentComplete event. Try to do that processing on the spot (inside your event handler) and see if it stil crashes. Some code posting would help too