[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Thread: access violation

Posted on 2005-05-04
17
Medium Priority
?
845 Views
Last Modified: 2013-11-20
Ah hello.

I have a threading problem.  First, an overview of what I am trying to achieve.

I have a view class with an associated document.  The document has a member variable that is an instance of a composite class, and is accesible from the view via

GetDocument()->GetData().

What I want to do is call a visitor on this variable from within my thread.

Here is what I am doing:

void CMyViewView::OnToolsCheckwizardcontent()
{
      CMainFrame* pMainFrame = (CMainFrame*)AfxGetMainWnd();
      CWinThread* pThread = AfxBeginThread(ThreadFunc, (LPVOID)pMainFrame->m_hWnd);
}

UINT CMyViewView::ThreadFunc(LPVOID pParam)
{
      CVisitorContentChecker v;
      CMainFrame* pMainFrame = (CMainFrame*)CWnd::FromHandle((HWND)pParam);
      
      CMyViewView* pView = (CMyViewView*)pMainFrame->GetActiveView();
      pView->GetDocument()->GetData()->Accept(v);
}

But this keeps breaking in GetActiveView()

call stack:

CObject::IsKindOf(const CRuntimeClass * pClass=0x00475454)  
CFrameWnd::GetActiveView()
CMyView::ThreadFunc(void * pParam=0x000f0a6a)

line causing trouble in IsKindOf():

CRuntimeClass* pClassThis = GetRuntimeClass();

result: Access violation reading location 0x0000111c.

How can I achieve what I am after, and is it a good idea ?

TIA
0
Comment
Question by:mrwad99
  • 9
  • 4
  • 4
17 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 13926920
I assume that 'pView' already is NULL. Is that the case?
0
 
LVL 19

Author Comment

by:mrwad99
ID: 13926968
Hi jkr,

well, the call stack is stuck on

CObject::IsKindOf(const CRuntimeClass * pClass=0x00475454)  

currently, but when I look back into the 'locals' section of

CMyView::ThreadFunc(void * pParam=0x000f0a6a)

I see pView listed as

pView      0xcccccccc {CMyView hWnd=???}      CMyView *

...?
0
 
LVL 86

Expert Comment

by:jkr
ID: 13927000
What is 'pMainFrame()'?
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 16

Assisted Solution

by:nonubik
nonubik earned 600 total points
ID: 13927009
Why don't you pass the pMainFrame as thread param?

...
     CWinThread* pThread = AfxBeginThread(ThreadFunc, (LPVOID)pMainFrame);
}

UINT CMyViewView::ThreadFunc(LPVOID pParam)
{
     CVisitorContentChecker v;
     CMainFrame* pMainFrame = (CMainFrame*)pParam;
...
0
 
LVL 86

Expert Comment

by:jkr
ID: 13927033
Oh, BTW, the problem is actually the line

CMainFrame* pMainFrame = (CMainFrame*)CWnd::FromHandle((HWND)pParam);

because - as http://support.microsoft.com/default.aspx?scid=kb;en-us;147578 ("CWnd Derived MFC Objects and Multi-threaded Applications") states - 'CWnd::FromHandle()' will fail:

"In a multi-threaded environment because windows are owned by threads, MFC keeps the temporary and permanent window handle map in thread local storage. The same is true for other handle maps like those for GDI objects and device contexts. Keeping the window handle maps in thread local storage ensures protection against simultaneous access by several threads.

The behavior of the functions CHandleMap::LookupPermanent() and CHandleMap::LookupTemporary() is a direct consequence of these facts. Given a window handle, these functions check the permanent and temporary window handle maps of the current thread for the existence of an associated CWnd derived MFC object. This means that if calls to these functions are made from a thread to search for MFC objects that represent windows created in other threads, these calls will fail. "
0
 
LVL 19

Author Comment

by:mrwad99
ID: 13927137
>> What is 'pMainFrame()'?

Er, can't see where I have declared exactly that; I have

CMainFrame* pMainFrame = (CMainFrame*)CWnd::FromHandle((HWND)pParam);

nonubik,

Yeah, fair comment.  I was originally sticking to passing HWNDs then recreating the CWnd from it due to the problem of some functions asserting and so forth due to not having a HWND for the CWnd passed in to the thread within the thread's handle map:

From Jeff Prosise's book:

"Going from a CWnd to an HWND is easy because the HWND is a data member of the CWnd, but going from an HWND to a CWnd can be done only through the handle maps. And here's the problem: Handle maps are local to each thread and aren't visible to other threads. If thread A created the CWnd whose address is passed to ASSERT_VALID, the corresponding HWND won't appear in thread B's permanent or temporary handle map and MFC will assert. Many of MFC's noninline member functions call ASSERT_VALID, but inline functions don't—at least not in current releases.

Frequently, MFC's assertions protect you from calling functions that wouldn't work anyway. In a release build, GetParentFrame returns NULL when called from a thread other than the one in which the parent frame was created. But in cases in which assertion errors are spurious—that is, in cases in which the function would work okay despite the per-thread handle tables—you can avoid assertions by passing real handles instead of object pointers. "

However, in this case passing the CMainFrame pointer works, but I now get an assertion in

CMyDoc* CMyView::GetDocument() // non-debug version is inline
{
      ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMyDoc)));  
      return (CMyDoc*)m_pDocument;
}

Huh ?
0
 
LVL 19

Author Comment

by:mrwad99
ID: 13927231
O....K.....

So it looks like somebody is telling minor porkies then...

If I go with MS and assume that I cannot solve the problem my way, or indeed nonubik's suggestion, how the heck do I do this ?
0
 
LVL 19

Author Comment

by:mrwad99
ID: 13927246
Futher extract from Jeff:

"For example, it's safe to call CWnd::GetTopLevelParent in a secondary thread if you call FromHandle first to create an entry in the thread's temporary handle map, as shown below.

CWinThread* pThread = AfxBeginThread (ThreadFunc, pWnd->m_hWnd);
   
UINT ThreadFunc (LPVOID pParam)
{
    CWnd* pWnd = CWnd::FromHandle ((HWND) pParam);
    CWnd* pParent = pWnd->GetTopLevelParent ();
    return 0;
}


That's why the MFC documentation warns that windows, GDI objects, and other objects should be passed between threads using handles instead of pointers. In general, you'll have fewer problems if you pass handles and use FromHandle to re-create objects in the destination threads. But don't take that to mean that just any function will work. It won't."

So is his last line,

"But don't take that to mean that just any function will work. It won't"

basically agreeing with MS in your post jkr ?
0
 
LVL 16

Expert Comment

by:nonubik
ID: 13927274
Why don't you just send a message from within the thread to the view or mainframe and on the handler to do what you need?
0
 
LVL 19

Author Comment

by:mrwad99
ID: 13927378
So you are suggesting:

1) Start thread up
2) Within ThreadFunc, post message MY_MESSAGE to View
3) In handler for MY_MESSAGE, create the visitor, get the document's data and let the visitor do its stuff

Would this not mean that the primary thread was doing all the visitors work though ?  If that is the case, then what is the need for the thread in the first place ?  My app will hang whilst the visitors work is done: not good.

?
0
 
LVL 16

Expert Comment

by:nonubik
ID: 13927394
Then make a worker thread within the 'Accept(v)' function.
0
 
LVL 86

Accepted Solution

by:
jkr earned 600 total points
ID: 13928249
Apart from what nonubik wrote, what abput passing the return value of 'GetDocument()->GetData()' to the thread?
0
 
LVL 19

Author Comment

by:mrwad99
ID: 13936327
OK, good idea jkr.

But what is this statement all about (from Jeff again)

"Now for the bad news about writing multithreaded MFC applications. As long as threads don't call member functions belonging to objects created by other threads, there are few restrictions on what they can do."

Accept is a member function of the object returned by GetData().

So if I have

UINT CMyView::ThreadFunc(LPVOID pParam)
{
      CVisitorContentChecker v;
      CThing* pThing = (CThing*)pParam;
      pThing->Accept(v);
}

is that not violating this ?
0
 
LVL 16

Expert Comment

by:nonubik
ID: 13936607
If you make Accept thread safe, no.(e.g. adding a critical section)
0
 
LVL 19

Author Comment

by:mrwad99
ID: 13943174
So the only problem would be if two threads were to simultaneously access the data, leading to obvious issues.

I am still unsure about the statement

"As long as threads don't call member functions belonging to objects created by other threads"

is the reason that this is bad because of the above (simulateneous access of data by two or more threads), or is there some other underlying issue, like what has been discovered with passing CWnd pointers above?

Thanks again.
0
 
LVL 19

Author Comment

by:mrwad99
ID: 13978613
Any comment on the above ?
0
 
LVL 19

Author Comment

by:mrwad99
ID: 14005793
Ta anyway.
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: Dynamic window placements and drawing on a form, simple usage of windows registry as a storage place for information. Continuing from the first article about sudoku.  There we have designed the application and put a lot of user int…
Ready to get certified? Check out some courses that help you prepare for third-party exams.
This Micro Tutorial will teach you how to add a cinematic look to any film or video out there. There are very few simple steps that you will follow to do so. This will be demonstrated using Adobe Premiere Pro CS6.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Suggested Courses
Course of the Month20 days, 8 hours left to enroll

867 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question