From: Joseph M. Newcomer on
See below...
On Mon, 03 May 2010 21:59:40 -0400, Hector Santos <sant9442(a)nospam.gmail.com> wrote:

>Joseph M. Newcomer wrote:
>
>> ****
>> If you pass a CWnd* across a thread boundary, you must not allow the CWnd object to be
>> deleted until the thread is done using it. This is critical, because what will happen is
>> the thread could end up accessing free storage which might actually be reallocated for
>> some other object.
>
>
>Isn't that general true as a rule of thumb of passing a pointer to a
>thread that could be become invalid outside the thread?
****
Yes. So I have often wondered why the rule seems to be particularly emphasized for CWnd*
Since I *always* pass a CWnd* to a thread, and have never had a problem as a consequence
(since I obey the limitation that the pointer cannot become invalid during the life of the
thread, and never use the HWND) I have wondered why the restriction is phrased in terms of
a CWnd *.
****
>
>What is CWnd* or HWND for the matter special in this regard?
****
Pointer or handle, both must remain valid if they are used by the thread. Any pointer,
any handle.
****
>
>Its a simple owner vs non-owner or z-order concept : if you didn't
>create it, you shouldn't delete it unless it was specifically creates
>for the thread to delete.
****
Ther are two rules here:
1. If you create it, only you delete it
2. (Agent model) You create it, and hand responsibility
to delete it to an agent

Note that for a CWnd*, only rule 1 should apply, because a thread cannot create an HWND
and pass responsibility to another thread (violates the :"don't try to use a window from a
non-owner thread" rule.

So if you follow the rule 1 above, and obey the rule that a pointer (or handle) must not
be invalidated during the life of a thread that uses it, and obey the rule that no thread
should interact with a window it doesn't own, then there is no problem passing a CWnd* to
a thread. So I don't understand why this is not the explanation given, instead of the
overly-restrictive "do not pass a CWnd* to a thread".
****
>
>Its also relates to cross process or dll boundaries, it is not
>recommended to create in one boundary and delete in another.
****
Yes, the rule 1 above.
****
>
>> But if you pass the HWND, the common failure mode is
>>
>>
>> class CMyView : public CView {
>> protected:
>> static UINT handler(LPVOID p);
>> ..
>> }
>>
>> AfxBeginThread(handler, this);
>>
>> /*static */ UINT CMyView::handler(LPVOID p)
>> {
>> CMyView * view = (CMyView *)p;
>>
>> // this works correctly
>> }
>>
>> But I've seen people who are told "You must pass the HWND because I once read a document
>> that said passing CWnd * is a Bad Idea" do the following
>>
>> AfxBeginThread(handler, (LPVOID)m_hWnd);
>>
>> /* static */ UINT CMyView::handler(LPVOID p)
>> {
>> CMyVIew * view = (CMyView*)CWnd::FromHandle((HWND)p);
>>
>> }
>>
>> OK, here's a Quickie Quiz: What's wrong with the above code? And why is it a Really
>> Really Bad Idea?
>
>
>Thats easy, two things:
>
>1) you are asking for something that is managed in some global table
>(a map in this case)
****
Actually, it isn't a global table, it is a per-thread table, and that's the problem.
****
>
>2) if its not found, a new one is created, and the thread will not
>know that fact in order to delete it.
****
The problem is that if it does not exist in the thread's local handle table, a temporary
object is created to represent it, and it is a generic CWnd* and cannot be cast to any
other type! (It is also a temporary object, and will be deleted at some point in the
undefined future; for a UI thread, when the thread pump enters the OnIdle handler)
****
>
>This is old material, but MFC historically used TLS to maintain its
>recording of Windows and handles per thread and across boundaries
>(i.e. a MFC DLL) that was problematic, especially for use MFC based
>objects, i.e, CString So in MFC 4.0, it uses a set of template
>classes to wrap process level data. For example, this is a little
>known trick and it works extremely work:
>
>struct CMyProcessData : public CNoTrackObject;
>{
> CString s;
>}
>struct CMyThreadData : public CNoTrackObject;
>{
> CString s;
>}
>CProcessLocal<CMyProcessData> MyProcessData;
>CThreadLocal<CMyThreadData> MyThreadData;
>
>void SetProcessString(LPCTSTR lpsz) { MyProcssData->s = lpsz; }
>void SetThreadString(LPCTSTR lpsz) { MyThreadData->s = lpsz; }
>CString GetProcessString() { return MyProcssData->s; }
>CString GetThreadString() { return MyThreadData->s; }
>
>When you start a thread, it will automatically get its own instance of
>CMyThreadData. I use this for a few dlls that creates threads and it
>allows me to maintain MFC safe process and thread state information
>when using MFC objects.
>
>This is documented in a MSDN Tech Note:
>
> TN058: MFC Module State Implementation.
> http://msdn.microsoft.com/en-us/library/ft1t4bbc(VS.80).aspx
>
>> This is one of those subtleties of where MFC Meets System Programming that I talk about in
>> my course.
>
>
>(RAISING HAND!)
>
>Isn't it better to as a general practice of pointer usage to
>understand how memory is used rather than making everything else fit
>under MFC?
****
If you want to use MFC objects in certain contexts, you need to understand how C++ manages
storage. It isn't so much an MFC issue as a C++ issue
****
>
>Understanding MFC is important, but following basic concepts of
>z-order programming inherently solves 99.99% (if not 100%) of the
>design questions to a point it doesn't become a question at all.
****
Yes, but knowing how to write the code to handle that is important; for example, the
OnClose handler must not call the superclass OnClose handler if the window has a CWnd* in
an active thread; since you cannot predict or control when OnClose is called, you must
make sure it does the right thing to handle what I believe you are calling the Z-order
programming issue.
****
>
>One of the problems (and goran we had this discussion before) with
>using higher layer tools, is that it increases the understanding
>requirements that can be become very subtle and quite often requires a
>DEEP understanding of the higher layer wrappings itself missing
>critical points of the basic underlining framework.
****
I agree. And my point is that you can't naively assume that the "framework magic" is
always going to work when you move outside what it is designed to handle (MFC isn't really
designed to support multithreading! Remember, the design of MFC goes back to Win16, which
had no threads)
****
>
>Example, this is something I'm currently working on with touches based
> with this idea of z-order and pointer ownerships and usage;
>Asynchronous RPC using a I/O completion port notification type. There
>is no MSDN example only a Event based notification type. Googling
>shows no results and the closest one is so complex with an extremely
>rich library that is dependent on so many other things that not even
>downloading the source and reviewing the code was difficult to see
>what is the basic framework to get it done.
>
>This required rolling up the sleeve and trying various ideas using the
>"raw" IOCP and ASYNC RPC API functions, re-reading the MSDN docs as
>you see results until you finally understand whats going on and get
>the layout right. I just now at this moment reached that point and
>took a timeout to read the MFC forum.
>
>The irony is now I will be wrapping it with my own classes to make it
>easy. I can only do that one you understand the basics.
****
Yep. We like to pretend that these nice frameworks hide all the grubby details, but in
fact, they make it even more necessary to understand the grubby details when you move
outside the base assumptions that were used in the design. MFC works perfectly well in a
single-threaded implementation, but if you write a muiltithreaded app in MFC, you are
courting disaster if you don't understand the inner workings of MFC.
joe
****
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Goran on
On May 4, 4:34 pm, Joseph M. Newcomer <newco...(a)flounder.com> wrote:
> >> The HWND in this case is less critical, because if you manage to destroy the HWND without
> >> deleting the CWnd *, any methods you invoke on the HWND (e.g., PostMessage) will
> >> assert-fail.  In a release version, the data posted will result, typically, in a storage
> >> leak, because the receiving window isn't there.  Serious, but not really fatal (until you
> >> run out of heap!)
>
> >Yes, of course. One should never ever allow a following sequence of
> >pointer ownership:
>
> >1. thread
> >2. "PostMessage" (e.g. pointer passed to it through WPARAM)
> >3. window/thread that receives the message
>
> ****
> Of course, this is the "agent" pattern which is implemented more formally in VS 2010.
> ****
>
> >... because PostMessage does not guarantee that message will be
> >delivered, even if HWND is correct and stable throughout. Letting
> >PostMessage "own" a heap pointer is a resource leak, end of.
>
> ****
> Actually, PostMessage *will* deliver the message to the queue.  It is important that the
> queue be processed.  The sending pattern should really resemble
>
>         Thing * t = new Thing;
>         if(!PostMessage(USER_DEFINED_MSG, (WPARAM)t)
>             { /* failed to enqueue */
>                      ...deal with failure in useful fashion
>              delete t;
>                      ...additional recovery
>             } /* failed to enqueue */

Yeah, I use this approach, too. The only problem I know with this is
that PostMessage can still fail even if HWND was ok. E.g. message
queue saturation ; that could easily count as programming error. But
it might fail for some other reason. What do I know about failure
modes of PostMessage, and even if I did, they might change in the
future, so avoiding that window of uncertain object ownership (while
message with the object in W/LPARAM is in queue) is still a good idea.

> If the window is destroyed before the messages are dequeued, this is essentially a
> programming error.  I handle this by using PostMessage to post the actual termination
> request, so it cannot be seen until alll messages have been dequeued; and this termination
> request is not posted until I receive a notification that the thread has terminated, so it
> cannot be simultaneously posting messages that would come after my termination request.
> ****
>
> >> But if you pass the HWND, the common failure mode is
>
> >> class CMyView : public CView {
> >>         protected:
> >>             static UINT handler(LPVOID p);
> >>    ..
>
> >> }
>
> >> AfxBeginThread(handler, this);
>
> >> /*static */ UINT CMyView::handler(LPVOID p)
> >>     {
> >>      CMyView * view = (CMyView *)p;
>
> >>      //  this works correctly
> >>     }
>
> >> But I've seen people who are told "You must pass the HWND because I once read a document
> >> that said passing CWnd * is a Bad Idea" do the following
>
> >> AfxBeginThread(handler, (LPVOID)m_hWnd);
>
> >> /* static */ UINT CMyView::handler(LPVOID p)
> >>    {
> >>     CMyVIew * view = (CMyView*)CWnd::FromHandle((HWND)p);
>
> >>    }
>
> >> OK, here's a Quickie Quiz: What's wrong with the above code?  And why is it a Really
> >> Really Bad Idea?
>
> >If thread is only posts messages before view's HWND gets destroyed,
> >there is no problem.
>
> ****
> See my above comment.  I guarantee that both the CWnd* and the HWND have a lifetime that
> exceeds the client threads.
> ****>If not, this might be hit by HWND reuse. Blind cast is completely
> >wrong (it presumes that it knows the type of the underlying CWnd) and
> >playing with HWND achieves strictly nothing.
>
> ****
> The blind cast converts what is actually a CWnd* to a CMyView*, so it is really in serious
> violation of sanity.  THis is because the FromHandle looks in the thread-local handle map,
> doesn't find the CWnd* associated with the HWND, and synthesizes a new CWnd* (so now there
> are TWO instances of a CWnd*-derived class wrapping the same HWND!)
>
> It also illustrates why blind casting is not a Really Good Idea!
> ****

Ah. I misunderstood, I thought that "Handler" code is supposed to be
running in the UI thread. (Except PostMessage, I avoid touching CWnd
stuff completely in the worker thread, so my mind is biased). Yes, if
this cast is in a thread that did not create/Attach the CWnd, this
really is a horrible idea.

But I see that my code snippets weren't clear, I'll try to be more
precise (but still: compiled with head-compiler and debugged with head-
debugger):

class CInput { /*whatever*/ };
typedef auto_ptr<CInput> AInput; // A == "auto ptr to..."
class COutput { /*whatever*/ };
typedef auto_ptr<COutput> AOutput;

class CSharedObject /*: public noncopyable? good idea*/
{
public:
CSharedObject(HWND wnd, ...) : m_hWnd(hWnd) {}
AInput GetInput() const {/*whatever*/}
AOutput GetOutput() const {/*whatever*/}
void SetInput(AInput&) {/*whatever*/}
void SetOutput(AOutput&) {/*whatever*/}
HWND GetHWnd() const { return hWnd; }
private:
HWND m_hWnd;
// whetever else
};
typedef shared_ptr<CSharedObject> SPSharedObject; // SP == "shared
pointer to..."
typedef weak_ptr<CSharedObject> WPSharedObject; // WP == "weak pointer
to..."

extern void PassToWorkerThread(const SPSharedObject& PSharedObject);

class CMyWnd : public CSomeMFCWnd
{
SPSharedObject m_PSharedObject;

void OnCreate(...) // Could be some other "window startup" function
{
if (-1==__super::OnCreate(...))
return -1;

// In production, following two lines need try {} catch(whatever)
{...; return -1; }
m_PSharedObject = SPSharedObject(new CSharedObject(m_hWnd, ...));
PassToWorkerThread(m_PSharedObject);

return 0;
}

void OnDestroy()
{
m_PSharedObject.reset();
__super.OnDestroy();
}

LRESULT OnMessageFromThread()
{
Display(m_PSharedObject->GetOutput());
}
void Display(const COutput& ) { whatever }

// MESSAGE_MAP etc...
};

WPSharedObject g_PWeak;
CCriticalSection g_CritSect;
void PassToWorkerThread(const SPSharedObject& PSharedObject)
{
CSingleLock L(&g_CritSect, TRUE);
g_PWeak = WPSharedObject(PSharedObject);
}
SPSharedObject GetSharedObjectInThread()
{
CSingleLock L(&g_CritSect, TRUE);
return g_PWeak.lock();
}

AOutput WorkWorkWork(const CInput& input)
{
// work long and hard...
return SomeAOutput;
}

UINT MyThreadProc(LPVOID p)
{ // in production, this needs "global" try {} catch(whatever) {}
while(ContinueRunning)
{
SPSharedObject PSharedObject(GetSharedObjectInThread());
if (PSharedObject)
{
PSharedObject->SetOuptut(WorkWorkWork(*PSharedObject-
>GetInput()));
::PostMessage(PSharedObject->GetHWnd(), 0, 0);
}
}

The above is the simplest possible form for the thread func. But
there, shared object stays alive while doing WorkWorkWork (because
there's PSharedObject that holds a reference to shared object). That
might be a bad idea if SharedObject holds important resources, so the
following form is IMO preferable:

UINT MyThreadProc(LPVOID p)
{ // still needs "global" try {} catch(whatever) {}
while(ContinueRunning)
{
AInput input;
{
SPSharedObject PSharedObject(GetSharedObjectInThread());
if (PSharedObject1)
input = PSharedObject.GetInput();
} // shared object ref held by PSharedObject released here
if (input.get())
{ // There was a "live" CSharedObject, process it
AOutput = WorkWorkWork(*input);
SPSharedObject PSharedObject(GetSharedObjectInThread());
if (PSharedObject)
{
PSharedObject->SetOuptut(Output));
::PostMessage(PSharedObject->GetHWnd(), 0, 0); // return value
willfully ignored
}
} // shared object ref held by PSharedObject released here
}

Some +/- obvious nitbits: in practice, thread needs to check
"ContinueRunning" more often, meaning that WorkWorkWork needs to do it
(best practice: don't check a flag, have a function that checks it and
throws "termination" exception, and have exception-safe code in the
thread; make that exception invisible to everybody else except the
thread function and the "termination check" function). Also,
WorkWorkWork might want to call GetSharedObjectInThread() periodically
to see whether it should abort because window was destroyed (it might
signal abortion by returning NULL AOutput, and that should be checked
to avoid useless PostMessage).

I hope this is more clear, code-wise. So, what do you say?

Goran.
From: Joseph M. Newcomer on
See below...
On Wed, 5 May 2010 03:05:16 -0700 (PDT), Goran <goran.pusic(a)gmail.com> wrote:

>On May 4, 4:34�pm, Joseph M. Newcomer <newco...(a)flounder.com> wrote:
>> >> The HWND in this case is less critical, because if you manage to destroy the HWND without
>> >> deleting the CWnd *, any methods you invoke on the HWND (e.g., PostMessage) will
>> >> assert-fail. �In a release version, the data posted will result, typically, in a storage
>> >> leak, because the receiving window isn't there. �Serious, but not really fatal (until you
>> >> run out of heap!)
>>
>> >Yes, of course. One should never ever allow a following sequence of
>> >pointer ownership:
>>
>> >1. thread
>> >2. "PostMessage" (e.g. pointer passed to it through WPARAM)
>> >3. window/thread that receives the message
>>
>> ****
>> Of course, this is the "agent" pattern which is implemented more formally in VS 2010.
>> ****
>>
>> >... because PostMessage does not guarantee that message will be
>> >delivered, even if HWND is correct and stable throughout. Letting
>> >PostMessage "own" a heap pointer is a resource leak, end of.
>>
>> ****
>> Actually, PostMessage *will* deliver the message to the queue. �It is important that the
>> queue be processed. �The sending pattern should really resemble
>>
>> � � � � Thing * t = new Thing;
>> � � � � if(!PostMessage(USER_DEFINED_MSG, (WPARAM)t)
>> � � � � � � { /* failed to enqueue */
>> � � � � � � � � � � �...deal with failure in useful fashion
>> � � � � � � �delete t;
>> � � � � � � � � � � �...additional recovery
>> � � � � � � } /* failed to enqueue */
>
>Yeah, I use this approach, too. The only problem I know with this is
>that PostMessage can still fail even if HWND was ok. E.g. message
>queue saturation ; that could easily count as programming error. But
>it might fail for some other reason. What do I know about failure
>modes of PostMessage, and even if I did, they might change in the
>future, so avoiding that window of uncertain object ownership (while
>message with the object in W/LPARAM is in queue) is still a good idea.
****
If the message queue is full, PostMessage returns FALSE. See my use of an IOCP to avoid
message queue saturation, as described in my essay on I/O Completion Ports.

I need to spend some time reading the code below...it does appear to be clearer, but I
need to work on it some more to get my head around it.
joe

****
>
>> If the window is destroyed before the messages are dequeued, this is essentially a
>> programming error. �I handle this by using PostMessage to post the actual termination
>> request, so it cannot be seen until alll messages have been dequeued; and this termination
>> request is not posted until I receive a notification that the thread has terminated, so it
>> cannot be simultaneously posting messages that would come after my termination request.
>> ****
>>
>> >> But if you pass the HWND, the common failure mode is
>>
>> >> class CMyView : public CView {
>> >> � � � � protected:
>> >> � � � � � � static UINT handler(LPVOID p);
>> >> � �..
>>
>> >> }
>>
>> >> AfxBeginThread(handler, this);
>>
>> >> /*static */ UINT CMyView::handler(LPVOID p)
>> >> � � {
>> >> � � �CMyView * view = (CMyView *)p;
>>
>> >> � � �// �this works correctly
>> >> � � }
>>
>> >> But I've seen people who are told "You must pass the HWND because I once read a document
>> >> that said passing CWnd * is a Bad Idea" do the following
>>
>> >> AfxBeginThread(handler, (LPVOID)m_hWnd);
>>
>> >> /* static */ UINT CMyView::handler(LPVOID p)
>> >> � �{
>> >> � � CMyVIew * view = (CMyView*)CWnd::FromHandle((HWND)p);
>>
>> >> � �}
>>
>> >> OK, here's a Quickie Quiz: What's wrong with the above code? �And why is it a Really
>> >> Really Bad Idea?
>>
>> >If thread is only posts messages before view's HWND gets destroyed,
>> >there is no problem.
>>
>> ****
>> See my above comment. �I guarantee that both the CWnd* and the HWND have a lifetime that
>> exceeds the client threads.
>> ****>If not, this might be hit by HWND reuse. Blind cast is completely
>> >wrong (it presumes that it knows the type of the underlying CWnd) and
>> >playing with HWND achieves strictly nothing.
>>
>> ****
>> The blind cast converts what is actually a CWnd* to a CMyView*, so it is really in serious
>> violation of sanity. �THis is because the FromHandle looks in the thread-local handle map,
>> doesn't find the CWnd* associated with the HWND, and synthesizes a new CWnd* (so now there
>> are TWO instances of a CWnd*-derived class wrapping the same HWND!)
>>
>> It also illustrates why blind casting is not a Really Good Idea!
>> ****
>
>Ah. I misunderstood, I thought that "Handler" code is supposed to be
>running in the UI thread. (Except PostMessage, I avoid touching CWnd
>stuff completely in the worker thread, so my mind is biased). Yes, if
>this cast is in a thread that did not create/Attach the CWnd, this
>really is a horrible idea.
>
>But I see that my code snippets weren't clear, I'll try to be more
>precise (but still: compiled with head-compiler and debugged with head-
>debugger):
>
>class CInput { /*whatever*/ };
>typedef auto_ptr<CInput> AInput; // A == "auto ptr to..."
>class COutput { /*whatever*/ };
>typedef auto_ptr<COutput> AOutput;
>
>class CSharedObject /*: public noncopyable? good idea*/
>{
>public:
> CSharedObject(HWND wnd, ...) : m_hWnd(hWnd) {}
> AInput GetInput() const {/*whatever*/}
> AOutput GetOutput() const {/*whatever*/}
> void SetInput(AInput&) {/*whatever*/}
> void SetOutput(AOutput&) {/*whatever*/}
> HWND GetHWnd() const { return hWnd; }
>private:
> HWND m_hWnd;
> // whetever else
>};
>typedef shared_ptr<CSharedObject> SPSharedObject; // SP == "shared
>pointer to..."
>typedef weak_ptr<CSharedObject> WPSharedObject; // WP == "weak pointer
>to..."
>
>extern void PassToWorkerThread(const SPSharedObject& PSharedObject);
>
>class CMyWnd : public CSomeMFCWnd
>{
> SPSharedObject m_PSharedObject;
>
> void OnCreate(...) // Could be some other "window startup" function
> {
> if (-1==__super::OnCreate(...))
> return -1;
>
> // In production, following two lines need try {} catch(whatever)
>{...; return -1; }
> m_PSharedObject = SPSharedObject(new CSharedObject(m_hWnd, ...));
> PassToWorkerThread(m_PSharedObject);
>
> return 0;
> }
>
> void OnDestroy()
> {
> m_PSharedObject.reset();
> __super.OnDestroy();
> }
>
> LRESULT OnMessageFromThread()
> {
> Display(m_PSharedObject->GetOutput());
> }
> void Display(const COutput& ) { whatever }
>
> // MESSAGE_MAP etc...
>};
>
>WPSharedObject g_PWeak;
>CCriticalSection g_CritSect;
>void PassToWorkerThread(const SPSharedObject& PSharedObject)
>{
> CSingleLock L(&g_CritSect, TRUE);
> g_PWeak = WPSharedObject(PSharedObject);
>}
>SPSharedObject GetSharedObjectInThread()
>{
> CSingleLock L(&g_CritSect, TRUE);
> return g_PWeak.lock();
>}
>
>AOutput WorkWorkWork(const CInput& input)
>{
> // work long and hard...
> return SomeAOutput;
>}
>
>UINT MyThreadProc(LPVOID p)
>{ // in production, this needs "global" try {} catch(whatever) {}
> while(ContinueRunning)
> {
> SPSharedObject PSharedObject(GetSharedObjectInThread());
> if (PSharedObject)
> {
> PSharedObject->SetOuptut(WorkWorkWork(*PSharedObject-
>>GetInput()));
> ::PostMessage(PSharedObject->GetHWnd(), 0, 0);
> }
>}
>
>The above is the simplest possible form for the thread func. But
>there, shared object stays alive while doing WorkWorkWork (because
>there's PSharedObject that holds a reference to shared object). That
>might be a bad idea if SharedObject holds important resources, so the
>following form is IMO preferable:
>
>UINT MyThreadProc(LPVOID p)
>{ // still needs "global" try {} catch(whatever) {}
> while(ContinueRunning)
> {
> AInput input;
> {
> SPSharedObject PSharedObject(GetSharedObjectInThread());
> if (PSharedObject1)
> input = PSharedObject.GetInput();
> } // shared object ref held by PSharedObject released here
> if (input.get())
> { // There was a "live" CSharedObject, process it
> AOutput = WorkWorkWork(*input);
> SPSharedObject PSharedObject(GetSharedObjectInThread());
> if (PSharedObject)
> {
> PSharedObject->SetOuptut(Output));
> ::PostMessage(PSharedObject->GetHWnd(), 0, 0); // return value
>willfully ignored
> }
> } // shared object ref held by PSharedObject released here
>}
>
>Some +/- obvious nitbits: in practice, thread needs to check
>"ContinueRunning" more often, meaning that WorkWorkWork needs to do it
>(best practice: don't check a flag, have a function that checks it and
>throws "termination" exception, and have exception-safe code in the
>thread; make that exception invisible to everybody else except the
>thread function and the "termination check" function). Also,
>WorkWorkWork might want to call GetSharedObjectInThread() periodically
>to see whether it should abort because window was destroyed (it might
>signal abortion by returning NULL AOutput, and that should be checked
>to avoid useless PostMessage).
>
>I hope this is more clear, code-wise. So, what do you say?
>
>Goran.
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Goran on
On May 5, 5:15 pm, Joseph M. Newcomer <newco...(a)flounder.com> wrote:
> If the message queue is full, PostMessage returns FALSE.  See my use of an IOCP to avoid
> message queue saturation, as described in my essay on I/O Completion Ports.

D-oh! Of course. Silly me. So one indeed can allow that pointer in the
message queue, provided that there is a window to handle it
eventually, right?

Goran.
From: Joseph M. Newcomer on
Yes. Key here is the window must continue to exist. So what I have is a boolean that
says whether or not a thread is running. The OnClose handler looks like

void CMyWnd::OnClose()
{
if(thread_running)
{
close_pending = TRUE;
return;
}
__super::OnClose()
}

The thread will post a UWM_THREAD_HAS_CLOSED notification when it is exiting.

LRESULT CMyWnd::ThreadHasClosed(WPARAM, LPARAM)
{
thread_running = FALSE;
if(close_pending)
PostMessage(WM_CLOSE);
return 0;
}

If there is an annoying delay in the thread terminating (rather than a fairly quick
turnaround, measuring in tens of milliseconds), I might hide the window in the OnClose
handler to give the user the illusion it has gone away.

Sometimes, I will create in the CDocument class a top-level window to handle
document-related messages. In this case, I'll defer the document closing.
joe

On Wed, 5 May 2010 10:03:56 -0700 (PDT), Goran <goran.pusic(a)gmail.com> wrote:

>On May 5, 5:15�pm, Joseph M. Newcomer <newco...(a)flounder.com> wrote:
>> If the message queue is full, PostMessage returns FALSE. �See my use of an IOCP to avoid
>> message queue saturation, as described in my essay on I/O Completion Ports.
>
>D-oh! Of course. Silly me. So one indeed can allow that pointer in the
>message queue, provided that there is a window to handle it
>eventually, right?
>
>Goran.
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm