Manage Timer on MFC [modified]
-
Hi, I'm developing an MFC SDI application. I use ActiveX and I wish to show a progressBar during the elaboration time of the AX ( for ex. while loading a report o populing a grid). I can manage the ProgressBar from all my app but not during the AX's aleborations. I try to use the timer on a thread, but I'm not able on it and I don't know if it's the better way for do what I need. Can somebody help me and preferibly post some sample code???:~ -- modified at 6:24 Wednesday 18th April, 2007
-
Hi, I'm developing an MFC SDI application. I use ActiveX and I wish to show a progressBar during the elaboration time of the AX ( for ex. while loading a report o populing a grid). I can manage the ProgressBar from all my app but not during the AX's aleborations. I try to use the timer on a thread, but I'm not able on it and I don't know if it's the better way for do what I need. Can somebody help me and preferibly post some sample code???:~ -- modified at 6:24 Wednesday 18th April, 2007
Not much info to go on here, but let me guess about what I think is going on... I'll just have a look inside my crystal orb... ;) In your application you have set up a Single Threaded Apartment (STA) and you're using an ActiveX control from that thread. At some point you're calling one of the interface methods of the ActiveX control commencing an operation that will take some time to complete. The function call will not return until the operation has completed. During this time you want to display a progress indicator and update it periodically. If the above is correctly guessed about your situation, then your main problem probably is that your application doesn't process messages while it's executing inside the ActiveX control. The call to the ActiveX is blocking and won't return until finished. This also prevents your user interface from being responsive as well. There's no sustainable way to fix this. Your main thread has got to process messages if you want to do GUI stuff in that thread. How it should be done: The ActiveX control should be implemented to be asyncronous. If an operation is started that will take a relatively long time to complete, the call should return immediately with information whether the operation could be started or not. When the operation completes, the ActiveX should fire an event through a connection point interface. It means that the ActiveX calls an interface implemented by the client. Progress information should be given to the client the same way, but through another method of course. What could be done: If the ActiveX control is not your own and you don't have access to the source code for it, there are some workarounds that may do the trick. The preferred solution at the top. 1. If the ActiveX does not have a GUI, you could spawn a UI-thread, initialize another STA and create the ActiveX in that thread. Marshal the interface for the ActiveX back to the main thread to be able to call other functions easily, but when starting lengthy operations you should post messages to the UI-thread so that thread becomes blocked instead. In your main thread you can update the progress indicator with a timer, typically with
CWnd::SetTimer(...)
. Post a message back to the main thread when the operation has finished. 2. Spawn a UI-thread from which you display a dialog box with a progress indicator while the main thread is blocked. Update the progress indicator with a timer in the dialog box. N.B. None of t -
Not much info to go on here, but let me guess about what I think is going on... I'll just have a look inside my crystal orb... ;) In your application you have set up a Single Threaded Apartment (STA) and you're using an ActiveX control from that thread. At some point you're calling one of the interface methods of the ActiveX control commencing an operation that will take some time to complete. The function call will not return until the operation has completed. During this time you want to display a progress indicator and update it periodically. If the above is correctly guessed about your situation, then your main problem probably is that your application doesn't process messages while it's executing inside the ActiveX control. The call to the ActiveX is blocking and won't return until finished. This also prevents your user interface from being responsive as well. There's no sustainable way to fix this. Your main thread has got to process messages if you want to do GUI stuff in that thread. How it should be done: The ActiveX control should be implemented to be asyncronous. If an operation is started that will take a relatively long time to complete, the call should return immediately with information whether the operation could be started or not. When the operation completes, the ActiveX should fire an event through a connection point interface. It means that the ActiveX calls an interface implemented by the client. Progress information should be given to the client the same way, but through another method of course. What could be done: If the ActiveX control is not your own and you don't have access to the source code for it, there are some workarounds that may do the trick. The preferred solution at the top. 1. If the ActiveX does not have a GUI, you could spawn a UI-thread, initialize another STA and create the ActiveX in that thread. Marshal the interface for the ActiveX back to the main thread to be able to call other functions easily, but when starting lengthy operations you should post messages to the UI-thread so that thread becomes blocked instead. In your main thread you can update the progress indicator with a timer, typically with
CWnd::SetTimer(...)
. Post a message back to the main thread when the operation has finished. 2. Spawn a UI-thread from which you display a dialog box with a progress indicator while the main thread is blocked. Update the progress indicator with a timer in the dialog box. N.B. None of tThanks for the answer, you check perfectelly my problem. I have read the article you link and I try to implement the first solutiona you've suggested. I made my CMyThread class derived from CWinThread class and create the thread in my app when I need to call the ActiveX functions. I have some problem: 1. In code when I need to create the Thread attach this code:
CMyThread * thread = AfxBeginThread(RUNTIME_CLASS(C4Thread));
but at compile-time it said "It's impossible to convert from 'CWinThread *' to 'CMyThread *'. WHY this? 2. In which method of my CMyThread class I must put the code to peform (the call to the ActiveX functions) or I need to implement a new function? In thi case how? Thanks a lot. -
Thanks for the answer, you check perfectelly my problem. I have read the article you link and I try to implement the first solutiona you've suggested. I made my CMyThread class derived from CWinThread class and create the thread in my app when I need to call the ActiveX functions. I have some problem: 1. In code when I need to create the Thread attach this code:
CMyThread * thread = AfxBeginThread(RUNTIME_CLASS(C4Thread));
but at compile-time it said "It's impossible to convert from 'CWinThread *' to 'CMyThread *'. WHY this? 2. In which method of my CMyThread class I must put the code to peform (the call to the ActiveX functions) or I need to implement a new function? In thi case how? Thanks a lot.CDRAIN wrote:
I try to implement the first solutiona you've suggested
Well, my first suggested solution wasn't not about UI-threads. It was about modifying the ActiveX control since it would be the best designed and easiest solution. Your problem is due to the ActiveX control being poorly designed, or you're using it in a way it wasn't intended. Do you have access to the source code for the ActiveX and can you make modifications to it? I don't want to encourage you to take the path to a less suitable solution (the UI-thread alternative) unless forced to. :->
"It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknown -
CDRAIN wrote:
I try to implement the first solutiona you've suggested
Well, my first suggested solution wasn't not about UI-threads. It was about modifying the ActiveX control since it would be the best designed and easiest solution. Your problem is due to the ActiveX control being poorly designed, or you're using it in a way it wasn't intended. Do you have access to the source code for the ActiveX and can you make modifications to it? I don't want to encourage you to take the path to a less suitable solution (the UI-thread alternative) unless forced to. :->
"It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknownSorry, I want to say that I try your second solution... I don't have access to the source code for the ActiveX, so I try the UI-Thread. I try to explain my problem: on my thread I must fill a grid control (the ActiveX) and while this i need to refresh a progress bar. This is the code I use: Create the thread in which I'll fill the grid
C4ThrGrid * thread = (C4ThrGrid *)AfxBeginThread(RUNTIME_CLASS(C4ThrGrid));
Call the thread's function that fill the gridthread->openGrid(&m_wndGrid,str,m_inUseSource);
Close the thread is it rightthread->PostThreadMessage(WM_QUIT, 0, 0);
Is it the right way to obtain what I need? Why at runtime when I create the thread appear a message box that said "Insufficent Memory" Help!!!! -
Sorry, I want to say that I try your second solution... I don't have access to the source code for the ActiveX, so I try the UI-Thread. I try to explain my problem: on my thread I must fill a grid control (the ActiveX) and while this i need to refresh a progress bar. This is the code I use: Create the thread in which I'll fill the grid
C4ThrGrid * thread = (C4ThrGrid *)AfxBeginThread(RUNTIME_CLASS(C4ThrGrid));
Call the thread's function that fill the gridthread->openGrid(&m_wndGrid,str,m_inUseSource);
Close the thread is it rightthread->PostThreadMessage(WM_QUIT, 0, 0);
Is it the right way to obtain what I need? Why at runtime when I create the thread appear a message box that said "Insufficent Memory" Help!!!!CDRAIN wrote:
Sorry, I want to say that I try your second solution...
Quite allright, I was only making sure that what I wrote made sense.
CDRAIN wrote:
I don't have access to the source code for the ActiveX, so I try the UI-Thread.
Ok. Too bad, though. This is, like I wrote earlier, not easily circumvented.
CDRAIN wrote:
Can you helo whit my problem?
Yes, I can, but I have a limited amount of time so you shouldn't rely on me solely. If you're not familiar with the things I mentioned in my earlier post, like UI-threads, STA, marshalling and so on; you should expect quite a learning curve before you're done with this. But consider that a great opportunity to learn! :-D Regarding your previous post: You don't need a
CMyThread*
member variable. You will only need aCWinThread*
variable. TheCMyThread
class is only needed for the implementation of what the thread should do, respond to messages and what messages to react upon. Changing the type toCWinThread*
will remove your compiler error. You don't "call" functions in a thread. You comunicate with a UI-thread by posting messages to and from the thread. Read the article I referred to again, carefully. ;) The article describes what you have to do, including where to call::CoInitialize()
and::CoUninitialize()
in order to create and tear down the STA. The first step is to successfully spawn the UI-thread and be able to stop it later. Before you're finished you also have to:- Marshal the ActiveX interface back to the main thread with calls to
::CoMarshalInterThreadInterfaceInStream(...)
and::CoGetInterfaceAndReleaseStream(...)
- Post a message to the UI-thread when the lengthy operation should be performed with PostThreadMessage(...)
- Set a timer to update your progress bar and implement the timer message handler with
CWnd::SetTimer(...)
and overrideCWnd::OnTimer(...)
- Post a message from the UI-thread when the lengthy operation has completed with
PostMessage(...)
- Release the interfaces when you're done in both the main thread and the UI-thread
- Terminate the UI-thread by posting a WM_QUIT message and wait for it to die with a call to
::WaitForSingl
- Marshal the ActiveX interface back to the main thread with calls to
-
CDRAIN wrote:
Sorry, I want to say that I try your second solution...
Quite allright, I was only making sure that what I wrote made sense.
CDRAIN wrote:
I don't have access to the source code for the ActiveX, so I try the UI-Thread.
Ok. Too bad, though. This is, like I wrote earlier, not easily circumvented.
CDRAIN wrote:
Can you helo whit my problem?
Yes, I can, but I have a limited amount of time so you shouldn't rely on me solely. If you're not familiar with the things I mentioned in my earlier post, like UI-threads, STA, marshalling and so on; you should expect quite a learning curve before you're done with this. But consider that a great opportunity to learn! :-D Regarding your previous post: You don't need a
CMyThread*
member variable. You will only need aCWinThread*
variable. TheCMyThread
class is only needed for the implementation of what the thread should do, respond to messages and what messages to react upon. Changing the type toCWinThread*
will remove your compiler error. You don't "call" functions in a thread. You comunicate with a UI-thread by posting messages to and from the thread. Read the article I referred to again, carefully. ;) The article describes what you have to do, including where to call::CoInitialize()
and::CoUninitialize()
in order to create and tear down the STA. The first step is to successfully spawn the UI-thread and be able to stop it later. Before you're finished you also have to:- Marshal the ActiveX interface back to the main thread with calls to
::CoMarshalInterThreadInterfaceInStream(...)
and::CoGetInterfaceAndReleaseStream(...)
- Post a message to the UI-thread when the lengthy operation should be performed with PostThreadMessage(...)
- Set a timer to update your progress bar and implement the timer message handler with
CWnd::SetTimer(...)
and overrideCWnd::OnTimer(...)
- Post a message from the UI-thread when the lengthy operation has completed with
PostMessage(...)
- Release the interfaces when you're done in both the main thread and the UI-thread
- Terminate the UI-thread by posting a WM_QUIT message and wait for it to die with a call to
::WaitForSingl
Thanks a lot for the help... ok I'm able to create a UI-Thread from my MainWnd as I need to get the ActiveX functionality. My Activex must fill the grid contained on the MainWnd, while the progressBar is incremented. So I set the timer, that increment the progressbar on the OnTimer event, and create the ui-thread using this code I use:
thread = new CMyThread(); //Set parameter I need on the ActiveX // Include the pointer to the grid **(is it right?)** thread->GripPointer= GridPointer; thread->par2 = par2; thread->par3 = par3; thread->CreateThread();
On my ui-thread code i fill the grid on the Run() method and at the end of the operation IPostMessage(..)
to the MainWnd and here I close the ui-thread sending aPostMessage(WM_INFORM_CLOSE, 0, 0);
. At Run-Time it gives me an error "Not valid pointer' when i call the function to fill the grid on the ui-thread. WHY? - Where I need to use the ::CoMarshalInterThreadInterfaceInStream(...) and ::CoGetInterfaceAndReleaseStream(...) method you said, and witch is they're scope? (Sorry I'm newer in this use :sigh:) - Have you an example code? Another question: But can I manage a graphic component of the MainWnd (the grid) from the ui-thread?? Is this my problem?:doh: Thanks a lot - Marshal the ActiveX interface back to the main thread with calls to
-
Thanks a lot for the help... ok I'm able to create a UI-Thread from my MainWnd as I need to get the ActiveX functionality. My Activex must fill the grid contained on the MainWnd, while the progressBar is incremented. So I set the timer, that increment the progressbar on the OnTimer event, and create the ui-thread using this code I use:
thread = new CMyThread(); //Set parameter I need on the ActiveX // Include the pointer to the grid **(is it right?)** thread->GripPointer= GridPointer; thread->par2 = par2; thread->par3 = par3; thread->CreateThread();
On my ui-thread code i fill the grid on the Run() method and at the end of the operation IPostMessage(..)
to the MainWnd and here I close the ui-thread sending aPostMessage(WM_INFORM_CLOSE, 0, 0);
. At Run-Time it gives me an error "Not valid pointer' when i call the function to fill the grid on the ui-thread. WHY? - Where I need to use the ::CoMarshalInterThreadInterfaceInStream(...) and ::CoGetInterfaceAndReleaseStream(...) method you said, and witch is they're scope? (Sorry I'm newer in this use :sigh:) - Have you an example code? Another question: But can I manage a graphic component of the MainWnd (the grid) from the ui-thread?? Is this my problem?:doh: Thanks a lotI'm afraid that this solution may be a little bit too complex. You ask about accessing graphic components in the main window from the UI-thread and whether this is a problem or not. The answer is "it depends on how you do it". You cannot access the components directly since your call is made from another thread and the object map is per thread basis. What you do is post
WM_COMMAND
messages to the different UI objects in the main window. I suggest that you revert to my alternative suggestion in my first post: spawn a thread that displays a dialog box with a progress bar prior to starting the lengthy operation from the main thread. This will save you the trouble of marshalling, sendingWM_COMMAND
messages to graphic components of the main window etc. Since it's not possible to implement the "best solution", i.e. refactor the ActiveX component and make it asynchronous, you don't have to make things more complex than necessary. It would be nice to have the ActiveX in another apartment as I suggested, but given your latest reply I realize that it's overly complicated. The user won't see any difference and in either case he/she won't be able to cancel the lengthy operation, so there's no point in having a "Cancel"-button in the dialog.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknown