Solved

How to terminate a multi-threaded program?

Posted on 1998-12-11
18
635 Views
Last Modified: 2013-12-03
Hello!
0
Comment
Question by:wfb
  • 8
  • 4
  • 4
  • +2
18 Comments
 
LVL 86

Expert Comment

by:jkr
Comment Utility
Well, i happened to write such an application not long ago (based on STL&RPC)  and did not encounter any problems - as the terrmination of any Win32 process should (and IMHO does) terminate all associated threads gracefully... but i assume that your problem is due to terminating a MFC app is not proper by just calling 'ExitProcess()' (which us usually sufficiant - unless a library such as MFC is involved and needs more 'cleanup') - so, could you be a bit more specific about the 'funny' error messages??
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
Wayne,

you could try PostQuitMessage. But normally ExitProcess should really do the job! If not, call TerminateProcess(GetCurrentProcess). But note, that this is not very clean.
I've never done any MFC programming, so I don't know from where the problems come. ExitProcess gives all loaded dlls the opportunity to unload themselves. So I guess, one of the dlls makes problems. You won't have this problem with TerminateProcess, because there the whole process, all threads and all dlls are stopped immediately. Of course there could be other problems like half written files and so on...
If your program handles the WM_QUIT message right, the best way would be PostQuitMessage.

Regards, Madshi.
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
P.S: Probably you'll have to send a WM_QUIT to the main thread, too. Use PostThreadMessage(mainThreadID,WM_QUIT,0,0) for that purpose.
0
 

Expert Comment

by:plsbctv
Comment Utility
Since you're writing both threads, this is easy.

When your license thread decides to quit the program have it send a WM_CLOSE to the main window. This will do the same thing as a mouse click on the close button and will give a clean shutdown with the chance to save files.

I would also be inclined to not make the license manager a separate thread. Start a Windows timer and do your check by catching the WM_TIMER message. This solves problem with terminating your thread.

    ++PLS
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
If you use WM_CLOSE, normally the program asks for saving files that are still open. So, I think, it's not the best method...
0
 

Author Comment

by:wfb
Comment Utility
Thanks for all the comments! However, I probably did not make it clear that I am only writing the license manager thread - other programmers are writing the "main thread" (i.e. the real application). The actual WIN32 application may written in either C or C++ and can be:

1) A console app.
2) A non-MFC window app.
3) An MFC-based window app.

Therefore, I can't rely on the application to respond to a message such as WM_CLOSE. Currently, the authors of the main applications only have to make ONE call to my license manager at program startup time. I would like to keep this interface very simple; I don't want to make application programmers modify their code in any significant way...

Usually, 'ExitProcess' does NOT generate any "strange" messages (e.g. something like "unable to access location x00000004") followed by an option to 'debug' or 'terminate' the program). SOME MFC-based programs, however, DO generate the above message (which will probably confuse a user and generate phone calls!) Note that the IDENTICAL program does NOT generate such a message UNLESS it is started via a 'Shortcut', which seems very, very odd.

When the application program is compiled and linked in 'debug' mode AND started via a Shortcut, an ASSERT error is produced inside the MFC routine 'DestroyWindow'. It appears that a NULL handle was detected, which caused the ASSERT to fail.

I don't really want to use 'TerminateProcess', because that does an even worse cleanup job. (The main application program is using a number of DLLs which, at the very least, should be notified of the shutdown.)

Any thoughts? (I'd be particularly interested in any theory as to why starting the program from a Shortcut makes such a big difference!)

Thanks,

Wayne

0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
Don't know why there should be a difference, when you start the program from a shell link.
Please tell us more about the error message. Click on the detail button. In which module/dll does it occur?

Regards, Madshi.
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
P.S:
Perhaps you should just try a PostThreadMessage(MainThreadID,WM_QUIT,0,0);
If it is a console window it will do no harm, otherwise it could help.
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
One (obvius?) difference when starting a program from a shell link is that (in 'newer' Win9x versions) the working directory is not the one the app resides in, but %WINDIR%\Desktop ...
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 

Author Comment

by:wfb
Comment Utility
As requested, here is the exact wording of the error message which pops up when I attempt to terminate one of our MFC-based programs that is started via a Shortcut. (This is the message from the DEBUG version of the program; the message from the RELEASE version is somewhat less useful.)

   Microsoft Visual C++ Debug Library

   Debug Assertion Failed!

   Program C:\Projects\adept2\Debug\adeptd.exe
   File: wincore.cpp
   Line: 950

   <snip>

   Abort     Retry     Ignore

Here is the code around line 950 in 'wincore.cpp':

BOOL CWnd::DestroyWindow()
{
      if (m_hWnd == NULL)
            return FALSE;

      CHandleMap* pMap = afxMapHWND();
      ASSERT(pMap != NULL); // <= LINE 950
      CWnd* pWnd = (CWnd*)pMap->LookupPermanent(m_hWnd);
#ifdef _DEBUG
      HWND hWndOrig = m_hWnd;
#endif

#ifdef _AFX_NO_OCC_SUPPORT
      BOOL bResult = ::DestroyWindow(m_hWnd);
#else //_AFX_NO_OCC_SUPPORT
      BOOL bResult;
      if (m_pCtrlSite == NULL)
            bResult = ::DestroyWindow(m_hWnd);
      else
            bResult = m_pCtrlSite->DestroyControl();
#endif //_AFX_NO_OCC_SUPPORT

---------------------------------------------

So far, I prefer Madshi's idea of using "PostThreadMessage (MainThreadID, WM_QUIT, 0, 0);" followed by an "ExitProcess" call in case the "PostThread" did not "take". QUESTION: How can I tell if the "PostThreadMessage" is actually shutting down the program? Do I just wait awhile and then make the "ExitProcess" call? (Sounds a little dangerous...)

Comment/question for 'jkr': The Shortcut is set so the program will start up in the directory containing the executable. Did you mean to say that a program will start up in the %WINDIR%\Desktop... directory if it IS NOT started from a Shortcut? (Recall that no message is generated if the program is started from the command line; it only seems to screw up when started from a Shortcut!)

More comments?


0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
You're right. Posting a WM_QUIT message and then directly calling ExitProcess afterwards is not senseless but also dangerous.
You could either wait some seconds, but that's of course not very nice, too.

I've experienced that some programs like the WM_QUIT stopping method, others not. So what do you think of that:
If I understood you right, some other programmers call you to add a thread that does the checking and stopping. So if they call you, they could give in a parameter like "useWMQuit: BOOL". If their program likes the WM_QUIT method, it's even much cleaner than ExitProcess, because then all the other threads can save their files and perform a totally clean shutdown.

Regards... Madshi.
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
Nope, i just recoginized different return values when calling 'GetCurrentDirectory()' and the program is started from a shortcut in which the working directory is not explicitly set (which probably ha nothing to do with you problem).

To clarify a bit:
ASSERT(pMap != NULL); // <= LINE 950
CWnd* pWnd = (CWnd*)pMap->LookupPermanent(m_hWnd);

Here MFC retrieves the 'CWnd*' belonging to your window from its 'permanent handle map' (there's also a temporary map that is used with 'CWnd::FromHandle()')
As your 'pMap' ovbviously points to oblivion, i assume a problem with the thread managment and MFC (which relies on certain conventions when using threads) - do you call 'AfxBeginThread()' or 'CreateThread()'? If the latter applier, we have found the problem ;-)
0
 

Author Comment

by:wfb
Comment Utility
I like the idea of using 'PostThreadMessage' to shutdown a Windows program and 'ExitProcess' to stop a console app. However, I'd rather not require the caller (i.e. the user) to specify what kind of program he is running. (Having such a requirement would change the documented interface to my license manager.) Surely it must be possible for a running program to determine whether or not it is a console application? (I'm afraid that 'GetActiveWindow' can't be used because:

1) The license manager thread may be started up before there IS an active window, and

2) The license thread itself does not have an active window, so 'GetActiveWindow' returns NULL if called from that thread.

Can anyone suggest a reliable way of determining the TYPE of a running program?

Thanks,  Wayne
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
You could use 'AllocConsole()', which will always fail in a console process - but take this only as a first hint ;-)
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
Hmmm. You could whether user32.dll is loaded "GetModuleHandle('user32')<>0". Since most API calls are hard linked, this should always work. Perhaps you can use AllocConsole (as jkr suggested). Have never written any console applications.

The second problem is: What can you do, if WM_QUIT does not work? I understand that you neither want the caller to decide the stop method nor want to change your interface. So probably, if it is no console application, you should post WM_QUIT, wait for 10 seconds and then call ExitProcess. It's not nice, but it should work. I think if the WM_QUIT is handled right, your thread should be ended automatically before it can call ExitProcess.

Regards... Madshi.
0
 

Author Comment

by:wfb
Comment Utility
The final solution...

It turns out that our programs are so various (console programs that call 'MessageBox', Windows programs that aren't always running a message loop, Windows programs that have an associated console window, etc.) that the ONLY solution which seems to work reliably is Madshi's:

In the license manager thread -

    Call PostThreadMessage with WM_QUIT
    Sleep for 10 seconds
    Call ExitProcess (in case the WM_QUIT didn't work)

This isn't the most elegant approach, but it DOES seem able to terminate our various programs without generating any "disturbing" messages.

I'd like to thanks everyone for their help. In particular, I'd like to award 125 points to 'Madshi' and 75 points to 'jkr' for their suggestions. (Do I need to fill in some form to make sure that these points are awarded properly?)

Thanks again,

Wayne

0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
Wayne,

hmmm. Perhaps you should ask this in the "Customer Service" forum. Have never splitted my points, so I don't know this...

Regards... Madshi.
0
 
LVL 7

Accepted Solution

by:
linda101698 earned 200 total points
Comment Utility
I am posting the solution found by wfb so it can be saved in the previously asked questions.  I'll add the points assigned to this question bak to his account so wfb can posted questions to award the points to the experts he would like to reward for their help.  wfb, see your customer service question on how to do this.

Linda Gardner
Customer Service @ Experts Exchange

Comment
     From: wfb
     Date: Tuesday, December 15 1998 - 10:18AM PST

     The final solution...

     It turns out that our programs are so various (console programs that call
     'MessageBox', Windows programs that aren't always running a message
     loop, Windows programs that have an associated console window, etc.) that
     the ONLY solution which seems to work reliably is Madshi's:

     In the license manager thread -

         Call PostThreadMessage with WM_QUIT
         Sleep for 10 seconds
         Call ExitProcess (in case the WM_QUIT didn't work)

     This isn't the most elegant approach, but it DOES seem able to terminate
     our various programs without generating any "disturbing" messages.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

In this article, I will show how to use the Ribbon IDs Tool Window to assign the built-in Office icons to a ribbon button.  This tool will help us to find the OfficeImageId that corresponds to our desired built-in Office icon. The tool is part of…
Entering time in Microsoft Access can be difficult. An input mask often bothers users more than helping them and won't catch all typing errors. This article shows how to create a textbox for 24-hour time input with full validation politely catching …
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.

762 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

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now