Perfectly understandable and doesn't require magic and/or threads.
The OnTimer() function is an event called upon the receipt of the "WM_TIMER" windows message. This message is dispatched by the MFC provided "message pump". This single WM_TIMER message was removed from the message queue prior to dispatch to OnTimer().
AfxMessageBox() does it's thing and waits for the "OK" button (or whatever) to be clicked. To detect the click, it needs to process windows messages for itself hence it provides it's own "message pump". Even though this message pump is provided by the message box itself, it will process *any* windows message for this application / thread family.
Along comes another "WM_TIMER" message for the next second and is dispatched to the same OnTimer() function.
Generally, when you are processing events, you run the processing to completion before returning to the "dispatch" portion of the message pump so you do not see these artifacts (throw a 10 or 20 second Sleep() in there instead of the message box and you'll see a more predictable sequence). In this case, AfxMessageBox() needed a message pump to complete it's work and you're timer ticks got processed by it.
Judicious use of KillTimer() and SetTimer() should be used if you want to interrupt the flow of ticks for whatever reason.
(Edited to answer another part of your question)
dlavrantonis wrote:
Where was this OnTimer instance stored? In the stack?
Yup, set a breakpoint on the
m_loglist.AddString(value);
statement *after* the message box appears. You'll get there for some number > 5. When you are there, examine the stack / call frame backwards, you'll eventually find the other call to OnTimer() for number 5. When you click on "OK", all you're doing is returning up through the stack.