Click here to Skip to main content
15,879,535 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
Hello, respected professionals from CodeProject.

I have a following problem:

I need to create a modeless dialog box, which starts a thread when one of its buttons is pressed.

That button will be hidden, until thread finishes. Then it will be shown.

Thread does lengthy job ( populates Excel ), so when I close my main app, although my dialog box and thread closes, Excel "zombie" process is left.

I have used MSDN example from here: http://support.microsoft.com/kb/216686[^] , and CodeProject article from here MS Office OLE Automation Using C++[^] , to learn OLE Automation.

I have found very similar question which helped me to start with threads here Abort thread properly when dialog box close button is clicked[^] , and I have also used an example about threads from Petzold's book Programming Windows 5th edition.

So far, it seems that everything works great, except for the underlined part.

I have tried sending the WM_CLOSE message to the dialog box to trigger thread abortion handler, but it doesn't work.

If I add a message box to the main window's WM_CLOSE handler, after sending the WM_CLOSE to the dialog box, all threads abort gracefully, I know that because I can see Excel closing in TaskManager, since I wait for them and only then click the message boxes OK button.

My question is:

Instead of a message box, how can I delay the closing of the main window, so my worker threads can abort gracefully ?

Thank you.

If code snippets are required, I will provide them. I have omitted them to keep this post short.

EDIT#1:


I have managed to implement proper thread function.

Code from the article above that fills Excel is inserted into thread function.

After each block of instructions a boolean variable is polled to see if thread function should be aborted.

In the thread function, data structure holding that boolean variable, is declared volatile, so compiler can not optimize the code, which would result in replacing while loop with if statement.

This solution is inspired by the example from Charles Petzold's legendary book, "Programming Windows" 5th edition.

Perhaps I could use event object, as it fits here better, but at this point I will satisfy myself with this solution, since I'm in a time trouble.

Thread handle is declared static, and is initialized to NULL in WM_INITDIALOG.

I have defined 2 custom messages, so thread function can inform dialog box if error occurred or a clean exit was performed.

These are defined as bellow:
C++
#define WM_THREAD_OK    ( WM_APP +1 )

#define WM_THREAD_ERROR ( WM_APP + 2 )


In my dialog box, either of these messages shows previously hidden Save button ( remember, to avoid multiple threads being fired off, I have hidden the button when user presses it and launches a thread ).

WM_CLOSE and IDCANCEL handlers are identical, and are implemented like this:

C++
case WM_CLOSE:

    if( threadHandle ) // thread is alive
    {
       obj.bContinue = false; // send abortion flag

       WaitForSingleObject( threadHandle, INFINITE );

       CloseHandle( threadHandle );

       threadHandle = NULL;

    }

    DestroyWindow(hwnd);

    return TRUE;
    break;


All I need to do now, is to rework my button handler.

I need to set thread handle to NULL, before I launch a new thread.

My question is:

Should I set the handle to NULL in response to WM_THREAD_OK message like this:

C++
case WM_THREAD_OK: // thread sent this

    if( threadHandle ) // just wait until it fully exits
    {
       WaitForSingleObject( threadHandle, INFINITE );

       CloseHandle( threadHandle );

       threadHandle = NULL;
    }

    ShowWindow( GetDlgItem( hwnd, IDC_BUTTON1 ), SW_SHOW );

    return TRUE;


or should I do it in my button handler like this:

C++
case IDC_BUTTON1:
  {
    ShowWindow( GetDlgItem( hwnd, IDC_SAVE ), SW_HIDE ); // hide button

    // fill the structure passed to thread function

    td.hwnd = hwnd;

    td.PrimaryKey = primaryKey;

    // create thread

    DWORD threadID;


    /*** THIS IS THE PART WHERE I NULL THE THREAD HANDLE ***/

    if( threadHandle ) // if thread is alive
    {
       // wait for it to finish

       WaitForSingleObject( threadHandle, INFINITE );

       // then NULL out the handle

       CloseHandle( threadHandle );

       threadHandle = NULL;
     }

     /********  AND THEN CREATE NEW THREAD *********/


     threadHandle = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)MyThread, 
                                        (void*)&td, 0, &threadID );

     if( !threadHandle )
     {
        MessageBox( hwnd, L"Error", L"Error", MB_ICONERROR );

        DestroyWindow(hwnd);
     }

  }
  break;


That is the only thing left, since I believe everything is in fine order now.

I would welcome a constructive advice/comment.

Thank you.
Posted
Updated 17-Sep-13 10:29am
v4
Comments
Sergey Alexandrovich Kryukov 18-Aug-13 18:18pm    
WM_CLOSE has nothing to do with thread abortion. It closes the window. And of course, if you did not care about closing threads, they will keep your application running. You don't want to "delay" anything, you need to close the threads. So, you formulation of the problem looks artificial.
—SA
AlwaysLearningNewStuff 18-Aug-13 18:50pm    
Thank you for your reply, but here is the problem:

When I open task manager, and launch threads, Excel appears in the list.

When I add message box in main window's WM_CLOSE handler, it enables me to wait for threads to be closed.

I know they get closed, since Excel is removed from the TaskManager's list.

Therefore, I believe I just need to delay main window's WM_CLOSE, so threads can have enough time to do proper cleanup.

This is my opinion, of course, if you have any other suggestion I would gladly hear you out.

Regards.
Sergey Alexandrovich Kryukov 18-Aug-13 19:47pm    
This is not a good approach. You need to design threads the way they could work correctly if the main window is closed. Send them notification and then close. Threads should terminate themselves later.
Got the idea?
—SA
AlwaysLearningNewStuff 18-Aug-13 19:56pm    
Aha...

So, on dialogs close event, say IDCANCEL, I should send some custom message to my worker thread, and when worker thread gets it, then it should abort itself gracefully.

Is that what you have meant ?

Thank you for reply.

Regards.
Sergey Alexandrovich Kryukov 18-Aug-13 20:11pm    
This is not event, this is the return value.
But yes, this is what I mean. The second idea is: your threads should not depend on UI. Then they can terminate when the UI is already closed.
—SA

Don't allows to close the application when modeless dialog is displayed.

Well, set a flag to indicate that you want to close the application and tell the modeless dialog to close (and this one also set a flag instead) and tell the thread to stop (preferably through a flag again).

When the thread exit, then close the modeless dialog and once the modeless dialog is closed the close the application.


I have not worked at the API lvel since a very long time. In .NET environment, those things are relatively trivial to implement thus it might be a bit harder in your case as you have to handle more subtil détails at that level.
 
Share this answer
 
Comments
pasztorpisti 19-Aug-13 4:12am    
+5, The simplest to implement solution from the many.
AlwaysLearningNewStuff 17-Sep-13 17:36pm    
I have tried to do things the way you have suggested.

I have updated the question with my current results.

Can you please take a look at it?

Edit is at the bottom of the question.

Thank you.
Philippe Mori 17-Sep-13 18:35pm    
You don't want to wait in IDC_BUTTON1 handler as it will essentially block the UI. Thus the approach using WM_THREAD_OK seems more appropriate.
AlwaysLearningNewStuff 18-Sep-13 4:52am    
Thank you.

As soon as I properly implement this, I will officially accept your solution.

Thank you for a fast reply.

Regards.
AlwaysLearningNewStuff 23-Sep-13 16:04pm    
Mr.Mori,

I have successfully implemented your suggestions, so I thank you for that.

The last question I have is concerned about closing the main window, while dialog box is open, and thread is running.

In my WM_CLOSE handler for main window, I have checked if dialog box is open, and if it is, then I have sent him the WM_CLOSE message.

In order to wait for dialog box to close, I keep sending my main window WM_CLOSE message, so it could continually check if the dialog box is closed ( dialog box is performing the closing of the thread properly ), and I exit if it is closed.

Here is the code snippet:


case WM_CLOSE:

// some cleanup operations

if( IsWindow( hDlg ) ) // if dialog is open
{
// close dialog box

SendMessage( hDlg, WM_CLOSE, 0, 0 );

// send close message to yourself
// so you can get here again and check if you can exit

SendMessage( hWnd, WM_CLOSE, 0, 0 );
}

// finally, dialog box is closed, so destroy window

DestroyWindow(hwnd);

return (LRESULT)0;


I just wanted to ask if there is a better solution, that you can suggest, before I accept your solution.

Thank you.

Regards.
Each of your threads should have an API that sets a boolean (or event or finite state machine status or equiv) that tells the thread that you want it to shut down. Any thread termination logic should really be handled by the thread itself. The thread's normal processing logic should poll the termination request periodically and act accordingly.

When you terminate a thread ungracefully from an external process, you do not give the thread an opportunity to deaccess resources or clean itself up properly. A lot of people kill off threads externally but I consider this flawed behavior.
 
Share this answer
 
Comments
pasztorpisti 19-Aug-13 5:13am    
This time I must disagree. A thread is an object and it has an owner. Usually the owner creates a thread and an owner destroys it. On destroying I mean graceful destroying that is usually one of the following:
1. Optionally sending a cancel signal to the thread and then waiting/joining the thread and finally deleting it.
2. Optionally sending a cancel singal and then later the thread sends a finish message to the owner (usually a message looped/gui thread) and in response the owner waits/joins the thread (that takes nearly zero time) and then deletes the thread object.

Breaking the thread ownership rule is the most common bug in multithreading programs. A started thread must be waited/joined at some point without exceptions.
H.Brydon 19-Aug-13 10:47am    
You said that you disagree, but you pretty much said what I said. My major point is to not use TerminateThread() from an external thread/process, but to somehow tell the thread to die and let it handle its own hospice details.
pasztorpisti 19-Aug-13 11:12am    
The TerminateThread part is okay. What I misunderstood is this: "Any thread termination logic should really be handled by the thread itself" - It thought you wanted to send a cancel signal to the thread and then let it to die alone but reading it again this is not the case. You just haven't mentioned the work to be done on the owner thread so your post is indeed okay. +5
In my codebase I don't have such problems because I usually do task based programming and thread creation is decoupled from my tasks. My different kind of thread pools (or "job/task executors") are created at program startup and they are halted when all tasks are finished and thread lifetime is managed by the pool itself.
Sergey Alexandrovich Kryukov 19-Aug-13 9:47am    
Good points, 5ed, but there is a lot more to it.
From the other hand, termination of a thread from outside of it is a must for a number of scenarios. I think that traditionally developers of OS and platforms ignored such needs, which I consider as the lack of understanding of the purpose of the OS by OS creators. After all, you know that killing of the process is traditionally allowed and does not disrupt OS integrity.

A while ago, looking for resolution of this problem, I found an article of one Microsoft developer (too bad, it's hard to find a reference now, but I should have it) where the very robust and save technique based on exception seeding was described. I implemented it, it works. It's not widely known and is not platform compatible (but based on documented Windows API, only platform-specific part of it). But now, this is put in practice in .NET Thread.Abort, which is nothing like brutal thread termination. Such technique is possible in native Windows code, but again...

—SA
H.Brydon 19-Aug-13 10:57am    
Likewise you seem to be disagreeing with me but you are saying the same thing that I did. A thread should not be killed externally but should be signalled to die and then wait for it to notice the request, handle it and die of its own accord.

External thread termination is bad juju.
AlwaysLearningNewStuff wrote:
…there was no saying about dialog box sending message to a worker thread
First of all, you cannot just "send message" to a thread.

The thread you want to "send message" two should define some mechanism to accept message. And the message queue could be as simple as the field accepting only one message (such as "it's time to terminate", "new data is available"). In particular, it should be circular, contain some loop checking message availability on each iteration. It may look like polling and often is, but this is not always the case. In particular, you may develop the mechanism the way to waste zero CPU time for waiting. You can throttle the thread and keep it in a wait state when there are no messages. This can be done using the event object. Please see:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682655%28v=vs.85%29.aspx[^],
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686915%28v=vs.85%29.aspx[^].

Now, most usual and more or less general communication method would be a blocking message queue which, by the way, may allow you to avoid locking of the access to shared data (in short, because event object is itself a thread synchronization primitive). Such queue is one of the main aspects of the producer-consumer problem:
https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem[^].

On Windows, one comprehensive approach is offered by Microsoft Asynchronous Agents Library: http://msdn.microsoft.com/en-us/library/dd492627%28v=vs.110%29.aspx[^].

It is possible that, for your purpose, such comprehensive approach would be an overkill. After all, it's possible that you may solve your problems with sending a "message" in only one instance (but you should thoroughly work out the scenario when you send a new message when the previous one is not yet consumed, so you would need to develop a mechanism where new one absorbs the old one). The synchronization would be done via an event object. You may also need locking, which on Windows should be represented by critical section as you want to synchronize the threads only on the same process; and critical section is the most light-weight variant of mutex (please see http://en.wikipedia.org/wiki/Mutual_exclusion[^]).

I would also advise development of a thread wrapper, to encapsulate such things as access and locking inside the wrapper class. This way, you can abstract out access to the thread.

—SA
 
Share this answer
 
Comments
[no name] 18-Aug-13 23:40pm    
It's all here. As I commented in OP's earlier post one must be in control of the threads one creates. Hypothesising that one needs a delay of indeterminate length is just evidence that one is not in control.
As everyone has said some design is required. 5.
Sergey Alexandrovich Kryukov 18-Aug-13 23:45pm    
Thank you. Yes, required :-)
And the idea of "delay" is really bad in principle. Good point, about "not in control".

I think all this talking about "sending a message" is stemmed from my advice not to "delay" and to handle WM_CLOSE instead of waiting. It requires some form of thread termination, which can be cooperative, via sending a thread some information of closed main window...

—SA
pasztorpisti 19-Aug-13 5:05am    
I think the delaying and the WM_CLOSE handling are the very same things in this case. OP said "delaying" because he probably doesn't know that this thing can be solved by handling WM_CLOSE.
Sergey Alexandrovich Kryukov 19-Aug-13 9:35am    
They say, "the soul of a stranger is darkness"... :-)
—SA
pasztorpisti 19-Aug-13 11:24am    
I usually like reading your posts because sometimes its composited like a well written novel despite that fact that you are probably not native english speaker. This time it was like the words of a jedy! :-)
You seem to be approaching a very complex area of programming with insufficient knowledge.

I read these articles about threads many years ago and have never looked back. They are worth reading. Any time you spend studying these will be well rewarded IMHO.

http://www.flounder.com/workerthreads.htm[^]

The problem of thread termination is well covered in the preceding.
 
Share this answer
 
v3
Comments
pasztorpisti 19-Aug-13 4:15am    
In MFC there are UI and Worker threads. Using secondary UI threads (your second link) is totally unnecessary and is an antipattern. The first link seems to be a good copy paste source though but copy pasting threading code without the required knowledge has always terrible results...
[no name] 19-Aug-13 4:27am    
Thanks - really intended to highlight the use of shutdown event not cut & paste. There may have been too much already.
pasztorpisti 19-Aug-13 5:21am    
Some may disagree with me but in my opinion using 1 thread for gui is enough. Why would someone want two threads with 2 message loops? The last time I've seen a gui UI threads I had trouble in fixing modality problems. The user interface can look like that of two different applications. If someone wants two UI threads then he wants two different programs. Its windows specific that each thread can have its own UI, many other frameworks support just 1 thread and that is quite enough. Probably your first sentence holds one of the best answers so I changed my mind: +5 :-)
Sergey Alexandrovich Kryukov 19-Aug-13 9:41am    
Agree, 5ed.
—SA
AlwaysLearningNewStuff 17-Sep-13 17:37pm    
I have tried to do things the way you have suggested.

I have updated the question with my current results.

Can you please take a look at it?

Edit is at the bottom of the question.

Thank you.
Very hard to give answers that help you to solve to problem because learning multithreading and becoming able to REALLY see behind the curtains requires months if not years in my opinion. There are many different solutions to your problem depending on what you consider acceptable behavior. One of these behaviors that is the simplest to implement was described by Philippe Mori: simply don't let your windows closed when worker threads are present in your system. If this behavior isn't good for you then continue reading this answer but I don't guarantee that you will be able to implement all solutions but at least you will have clue on what is an acceptable and viable solution in your case.

Some basic information about WM_CLOSE before explanation about threads: If someone presses X on your window or dialog then your window receives WM_SYSCOMMAND/SC_CLOSE that is converted into WM_CLOSE (by the DefWindowProc if you dont handle SC_CLOSE directly). If some presses Alt+F4 then your window receives WM_CLOSE directly. Additionally your window can receive directly posted/sent WM_CLOSE message from your code from any threads or from any threads of external processes. WM_CLOSE is nothing more: its like a button press event, you can simply ignore it if you want however that would really break the behavior the users expect. But wait, WM_CLOSE closes/destroys the window, isn't it??? If you don't handle WM_CLOSE or you call the default handler too after your code then DefWindowProc is called with WM_CLOSE where the default behavior is calling DestroyWindow on your window/dialog that you don't want in many cases. In MFC destroying your main window means the termination of your program.
So the conclusion: WM_CLOSE is just a request, a "button press event" that you can ignore and take default behavior depending on the "state of your program" from the perspective of your gui thread. For example many applications use WM_CLOSE to prevent application close when you haven't saved the document, you can use WM_CLOSE to popup a messagebox.

If you don't want your application or maybe your dialog to be closed then just handle WM_CLOSE and instead of calling the default handler (that would destroy the window) you check whether a thread is running and if you found some active worker threads than you take one of the following actions:

EDIT: In all cases immediately send a cancel message/set a (synchronized!!!) cancel flag to singal your thread that it should terminate gracefully as quick as possible! In some cases a thread is not cancellable, in this case skip this step...
After this take one of the following actions:

1. Popup a messagebox that "Sorry, XYZ is still in progress, try again later". This is the easiest solution recommended by Philippe.

2. You may want to implement automatic exit when workers terminate. You can take any action that works only on the state of the gui side, you can always change gui state as you wish on the gui thread. For example after setting the "autoexit flag" you can show a "dialog/panel" that ignores every button/mouse input along with WM_CLOSE with the label: "Exiting", or you can hide all windows: hiding isn't the same as destroying and hiding is also just a gui state change!
OK, we set the autoexit flag and we changed the gui state, whats next??? The autoexit flag and all other gui related variables/states are accessable only from the gui thread. This means that you will need a gui event (like WM_CLOSE and its friends) to handle this autoexit. But what kind of gui event will this be??? You have N worker threads and you have some info from these on the gui side data (accessible fromt he gui thread) even if this data is only the fact that these threads exist and has been started. The best way to solve this event problem in a gui program is sending a message to a window of the gui program from the thread at the end of the threadproc. This can be a simple custom window message for example to the main window like (WM_USER+5). Any worker thread is allowed to use PostMessage or SendMessage (I recommend PostMessage) to send a message to a gui thread (and you can send for example a thread pointer or thread id or whatever as wParam of the message). This way we can handle this message on the gui side and we can update our gui-side data about the threads. When you recieve this thread-finished message you have to take the following actions:
- Wait for the thread to terminate. You have to do this. Since the thread Posted (not Sent!!!) this message from the end of its threadproc this wait wont take long so you gui wont hang up! You have to wait/join every worker thread that you have started (this is the ownership thing, you are in control with this as others mentioned)! Period.
- Since we received this message we know that we have 1 less worker thread. If the number of worker threads have reached zero then lets check the autoexit flag: if its set then you can start program shutdown (that you previous cancelled by handling WM_CLOSE) this way: http://support.microsoft.com/kb/117320[^]

In MFC there are two AfxBeginThread() functions, if you don't want to freeze because of calling some MFC lib funcs or some libraries that make use of MFC libs then use the worker thread version of this AfxBeginThread() functions to create your thread instead of some WinAPI/crt CreateThread/beginthread variants. I recommended the worker thread version because secondary ui threads are useless and they are just a wasting resources.
 
Share this answer
 
v3
Comments
Sergey Alexandrovich Kryukov 19-Aug-13 9:51am    
Good points, a 5. Now, about learning it: I doubt it has to be years. It could be much faster if taught well. Unfortunately, I never saw that it was taught well enough anywhere; maybe I just don't know... These days, you can have comprehensive material in literature; the problem is: I never saw it all in one manual; again, may be I'm just unaware of some.
—SA
pasztorpisti 19-Aug-13 10:59am    
Thank you! There is a lot of overlap between this and my answer in the link sent by OP.
I'm afraid you are right. I've realized through the years that there are some golden rules and good patterns to follow by forgetting most of the terrible things taught by tutorials. Just like in case of many other topics: threading has a lot of tutorials and material available, most of them are incomplete, totally unsuitable, and buggy: just the observations/blogpost of someone who has successfully copy-pasted together something buggy that seems to work most of the time. There are many other good tutorials that are unsuitable for other reasons: Starts by explaining caches and synchronization primitives by explaining locks without actually introducing their interface... and tutorials with lot of similar huge mistakes. Most of the tutorials are API specific and most of the API calls are just there to make antipatterns attractive (Suspend/ResumeThread, ExitThread, TerminateThread, try_lock, ...) while most of these APIs are just there for system diagnostic and debugging (for example SuspendThread is usefule because you can get a context only for a suspended thread in a debugger or assert/crash handler but beginners think that its fun to pause their background file copy this way...). The lot of material is useless if you have a lot of false unecessary info in it. And what about patterns like Async Methods, IOU, Task Based Programming, Flow Based Programming, .... Most of these are overlapping but it is often not clear for beginners that these are solutions mostly to the same problem just from a different perspective. I've seen people who thought these are separate solutions to different problems... And partly they were right, but...
Its simply so many info for a beginner starting from low-to-high level concepts without a "Table Of Contents" that they simply cant make the distinction between these categories and most importantly good and bad. Many tutorials and low level APIs help in misleading beginners (and not only beginners!) to the wrong direction. I'm pretty sure that there are many people who think they can program multithreading but in fact they have problems with basic things like sharing too much data unnecessarily (tutorials teach that sharing with locks is okay isnt it???) and building/destroying thread objects gracefully, placing all this threading stuff well into an object oriented design... I'm writing a multithreading tutorial that tries to help both beginners and "lost veterans" of the latter type but I'm so lazy and its so hard to build up a tutorial in this topic that I haven't progressed much in the last 2 years, ... Now I've started to invest more and more time in it again starting from last month.
Sergey Alexandrovich Kryukov 19-Aug-13 11:07am    
Sad but true...
—SA
AlwaysLearningNewStuff 17-Sep-13 17:37pm    
I have tried to do things the way you have suggested.

I have updated the question with my current results.

Can you please take a look at it?

Edit is at the bottom of the question.

Thank you.
AlwaysLearningNewStuff 24-Sep-13 11:14am    
Solution suggested to me by the member mbue has helped me to grasp what I must do.

Now your post makes sense.

Thank you for your efforts.

+5 from me.
The only thing you have to do is: dont leave the main message loop until the excel-interface has been released. Is the excel interface used in a thread, please wait until the thread has been terminated. You can delay the main windows close message if you dont delagate the message to the default window proc. The default window proc will destroy the window. Is the thread already running check it and post the message again.

See the following code:
C++
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>

#pragma comment (lib,"user32.lib")
#pragma comment (lib,"ole32.lib")
#pragma comment (lib,"oleaut32.lib")

#pragma warning(disable:4146 4049 4786)
#include "MSO97.tlh"
#include "VBEEXT1.tlh"
#include "EXCEL8.tlh"
#pragma warning(default:4146 4049 4786)

LRESULT FAR PASCAL WndProc(HWND h,unsigned int m,WPARAM w,LPARAM l);

typedef struct
{
  HANDLE    hTerm;
  HANDLE    hThread;

} APPDATA;

int FAR PASCAL _tWinMain(HINSTANCE h,HINSTANCE p,LPTSTR c,int sw)
{
  WNDCLASS  wc;
  HWND      hwnd;
  MSG        m;
  APPDATA    app = { 0,0 };

  wc.style         = CS_DBLCLKS;
  wc.lpfnWndProc   = WndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = h;
  wc.hIcon         = 0;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  wc.lpszMenuName  = 0;
  wc.lpszClassName = __TEXT("ThreadMain");

  RegisterClass(&wc);

  app.hTerm = CreateEvent(0,0,0,0);
  hwnd = CreateWindowEx
  (
    0,
    wc.lpszClassName,
    __TEXT("Main window"),
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    (HWND)0,
    (HMENU)0,
    wc.hInstance,
    (void*)&app
  );
  if(IsWindow(hwnd))
  {
    ShowWindow(hwnd,sw);
    // message loop
    while(GetMessage(&m,0,0,0))
    {
      TranslateMessage(&m);
      DispatchMessage(&m);
    }
    // if thread is already running
    if(app.hThread)
    {
      SetEvent(app.hTerm);
      WaitForSingleObject(app.hThread,INFINITE);
      CloseHandle(app.hThread);
    }
  }
  CloseHandle(app.hTerm);
  return 0;
}

LRESULT FAR PASCAL ExcelThread(void* p)
{
  HANDLE        hterm = (HANDLE)p;
  HRESULT        hr;
  
  if(S_OK==CoInitializeEx(0,COINIT_MULTITHREADED))
  {
    Excel::_ApplicationPtr    excelapp;

    // start excel
    excelapp.CreateInstance(__TEXT("Excel.Application"),0,CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER);
    if(excelapp)
    {
      // show excel
      excelapp->Visible = 1;

      // do anything you want to do with excel
      // but wrap it into try...catch
      // ...................

      // this loop waits until excel is closed
      while(WAIT_TIMEOUT==WaitForSingleObject(hterm,1000))
      {
        /**/ try {
        if(!excelapp->Visible)
          break;
        /**/ } catch(...){}
      }
      // finally on stop thread:
      // bring excel to front
      /**/ try {
      excelapp->WindowState = Excel::xlMinimized;
      excelapp->WindowState = Excel::xlNormal;
      /**/ } catch(...){}

      // close all sheets
      /**/ try {
      while(excelapp->ActiveWorkbook)
      {
        excelapp->ActiveWorkbook->Close();
      }
      /**/ } catch(...){}
      // cleanup
      hr = excelapp->Quit();

    }
    CoUninitialize();
  }

  return 0;
}

// dialog proc
int FAR PASCAL DlgProc(HWND h,unsigned int m,WPARAM w,LPARAM l)
{
  unsigned long    tid;
  APPDATA*        app = (APPDATA*)GetWindowLong(h,DWL_USER);
  switch(m)
  {
    case WM_INITDIALOG: SetWindowLong(h,DWL_USER,(LONG)l); break;
    case WM_CLOSE     : DestroyWindow(h); break;
    case WM_COMMAND   :
      switch(LOWORD(w))
      {
        case 100: // start thread
          if(app->hThread)
          {
            if(WAIT_TIMEOUT==WaitForSingleObject(app->hThread,1))
              break;
            CloseHandle(app->hThread);
            app->hThread = 0;
          }
          app->hThread = CreateThread(0,0,(LPTHREAD_START_ROUTINE)ExcelThread,app->hTerm,0,&tid);
        break;
      }
    break;
  }
  return 0;
}

// tool dialog
void ShowThreadDialog(HWND hParent,APPDATA* app)
{
  HWND            hdlg;
  HINSTANCE        hinst = (HINSTANCE)GetWindowLong(hParent,GWL_HINSTANCE);
  HRSRC            hres = FindResource(hinst,MAKEINTRESOURCE(100),RT_DIALOG);
  HGLOBAL          hmem = hres ? LoadResource(hinst,hres) : 0;

  if(hres)
  {
    hdlg = CreateDialogIndirectParam(hinst,(DLGTEMPLATE*)LockResource(hmem),hParent,DlgProc,(LPARAM)app);
    if(IsWindow(hdlg))
    {
      ShowWindow(hdlg,SW_SHOW);
    }
  }
}

// window messages
void WmCreate(HWND h,APPDATA* app)
{
  SetWindowLong(h,GWL_USERDATA,(LONG)app);
  ShowThreadDialog(h,app);
}

void WmClose(HWND h,APPDATA* app)
{
  if(app && app->hThread)
  {
    // is thread running
    if(WAIT_TIMEOUT!=WaitForSingleObject(app->hThread,1))
    {
      CloseHandle(app->hThread);
      app->hThread = 0;
      DestroyWindow(h);
    }
    else
    {
      // try to stop thread
      SetEvent(app->hTerm);
      // and try it again
      PostMessage(h,WM_CLOSE,0,0);
    }
  }
  else DestroyWindow(h);
}

// main window proc
LRESULT FAR PASCAL WndProc(HWND h,unsigned int m,WPARAM w,LPARAM l)
{
  APPDATA*  app = (APPDATA*)GetWindowLong(h,GWL_USERDATA);
  switch(m)
  {
    case WM_CREATE       : WmCreate(h,(APPDATA*)((CREATESTRUCT*)l)->lpCreateParams); break;
    case WM_CLOSE        : WmClose(h,app); return 0// do not call the default proc
    case WM_DESTROY      : PostQuitMessage(0); break;
  }
  return DefWindowProc(h,m,w,l);
}

Good luck.
 
Share this answer
 
Comments
pasztorpisti 19-Aug-13 4:19am    
Not using AfxBeginThread() (the worker version of the overloads, not the UI) might result in not initializing some TLS slots inside MFC for your worker thread that means: you may get bugs and crashes if you call into some MFC libraries (directly, or indirectly, let it be UI related or non-ui related MFC code) that make use of these TLS slots.
EDIT: I assumed that the user is using some kind of gui framework (MFC) because today WinAPI gui programming is not too popular. We will see... You answer contains half of the truth: Every thread must be waited/joined at some point before exiting the application.
AlwaysLearningNewStuff 17-Sep-13 17:39pm    
I have tried to do things the way you have suggested.

I have updated the question with my current results.

Can you please take a look at it?

Edit is at the bottom of the question.

Thank you.
mbue 19-Sep-13 7:44am    
hello AlwaysLearningNewStuff:
I would suggest you to stay in the message loop handler as short as possible.
If the thread signals its state by WM_THREAD_OK your message handler should test the thread state and return.

case WM_THREAD_OK: // thread sent this

if( IsThreadRunning() ) // just wait until it fully exits
{
PostMessage(hwnd,WM_THREAD_OK,0,0); // thread is already running post the message again
// alternative: use a timer to check the threads state
}
else
{
ThreadFinished(hwnd);
}

return TRUE;

case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_BUTTON1:
StartThread(hwnd);
return 0;
}
break;



Encapsulate the event handling into functions. Show the user whats going on by setting the buttons state and text.

int IsThreadRunning()
{
if(threadHandle)
{
if(WAIT_TIMEOUT==WaitForSingleObject( threadHandle, 1 ))
return 1;

CloseHandle( threadHandle );
threadHandle = NULL;
}
return 0;
}

void ThreadFinished(HWND hDlg)
{
HWND hbtn = GetDlgItem( hDlg, IDC_BUTTON1 );

// show the user whats going on
EnableWindow( hbtn, 1 );
SetWindowText(hbtn,__TEXT("start thread"));
}

void ThreadStarted(HWND hDlg)
{
HWND hbtn = GetDlgItem( hDlg, IDC_BUTTON1 );

// show the user whats going on
EnableWindow( hbtn, 0 );
SetWindowText(hbtn,__TEXT("thread running"));
}

void StartThread(HWND hDlg)
{
if(!IsThreadRunning())
{
DWORD threadID;

td.hwnd = hDlg;
td.PrimaryKey = primaryKey;
threadHandle = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)MyThread, (void*)&td, 0, &threadID );

if( !threadHandle )
{
MessageBox( hDlg, L"Error", L"Error", MB_ICONERROR );
DestroyWindow(hDlg);
}
else
{
ThreadStarted(hDlg);
}
}
}



Regards.
AlwaysLearningNewStuff 19-Sep-13 12:20pm    
Thank you, that is similar to what I had in mind.

Now only the main window's WM_CLOSE remains to be handled!

Your answer will be accepted after I finish that, be sure.

Thank you so much!

Regards.
AlwaysLearningNewStuff 23-Sep-13 16:17pm    
Mr.mbue,

I have successfully implemented your suggestions, so I thank you for that.

The last question I have is concerned about closing the main window, while dialog box is open, and thread is running.

In my WM_CLOSE handler for main window, I have checked if dialog box is open, and if it is, then I have sent him the WM_CLOSE message.

In order to wait for dialog box to close, I keep sending my main window WM_CLOSE message, so it could continually check if the dialog box is closed ( dialog box is performing the closing of the thread properly ), and I exit if it is closed.

Here is the code snippet:


case WM_CLOSE:

// some cleanup operations

if( IsWindow( hDlg ) ) // if dialog is open
{
// close dialog box

SendMessage( hDlg, WM_CLOSE, 0, 0 );

// send close message to yourself
// so you can get here again and check if you can exit

SendMessage( hWnd, WM_CLOSE, 0, 0 );
}

// finally, dialog box is closed, so destroy window

DestroyWindow(hwnd);

return (LRESULT)0;


I just wanted to ask if there is a better solution, that you can suggest, before I accept your solution.

Thank you.

Regards.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900