If I were running a CVI DLL code module from TestStand using the CVI adapter and terminated it somewhere in the code module by using the Windows SDK ExitProcess(0); command it will return to TestStand and generate an ActiveX error as follows:
" -17702; An error occurred accessing the CVI ActiveX automation server.The CVI adapter will attempt to reconnect on next execution."
It appears any subsequent communication to the CVI server in this same execution (i.e. by other steps that follow) will fail also until the execution is re-started. Is there a way to reset/restart the communication in this sequence's execution between TestStand steps? i.e can the CVI adapter reconnect on this execution. How does TestStand re-establ ish communication with the CVI server using the CVI adapter ? Does it have to destroy that object and create a new one to recover ?
To Donahut - Keep in mind that if you were not running the CVI DLL in an external instance of CVI, your call to ExitProcess would kill the sequence editor or operator interface. Calling ExitProcess is very severe. By calling this you have cutout the external instance of CVI that TestStand is using and in general TestStand can only recover from this by starting the execution over again. Basically we loose all information about the external DLL calls that CVI is making for it.
If you cannot gracefully leave the function you are calling from TestStand, and you want to jump out of a code module and have TestStand catch it and generate an error, you need to throw an exception. CVI written code typically does not catch exceptions but the debugger may.
I n CVI you could raise an exception by calling the SDK function RaiseException in Kernel32.dll.
Scott, Thank you for the quick reply. I do not think this will work though on page 3-6 of the LabWindows CVI programmers reference manual it states structured exception handling can not be used in LabWindows/CVI. What we are really trying to accomplish is to have a button on the operator interface that will terminate the code module immediately. The ActiveX button provided by TestStand only signal's to TestStand to terminate when the currently executing code module completes. If this code module takes several minutes to complete this is to long. We may need to shutdown power immediately if there is an emergency. Even if we polled for TestStand terminate event in our code module every so often you would still have the same problem if an instrument took a long time to complete its i/o operation like a cal or self-test. We were hoping to use the SDK function ExitProcess which does kill our code module instantly but like you said and we already found out it will kill the operator interface also if we are running the code module in process. Even if we run it out of process when we return to test stand we get the ActiveX error as noted earlier (This does not seem easily recoverable as you stated). We could ignore the error and go on to cleanup but the in-process issue still exists and we would like to use it both in-process and out of process. Any ideas would be appreciated.
You stated that you want a button on the operator interface to immediately terminate a code module. Keep in mind that the operator interface is running in what I would call the GUI thread, and all executions run in there own threads. In the case of using an external instance of CVI to run code modules, the external instance process has its own threads to execute code modules.
You may already understand this, but if an execution thread is busy running inside a code module, the code module must be told to complete in some way. The only way for this to occur is that the code module must be written to monitor for a state change that signals that it must stop doing its work. If the thread is busy off in some third party code lik e talking to an instrument, the thread cannot see the state change until the third party code completes.
I am confused about something. You cannot call KillProcess inside the code module unless the code module is actively running code that you have control over. How do you expect the button on the operator interface to instruct the code module to call KillProcess?
Regarding exceptions, you are correct in that CVI does not support structured exception handling. The primary reason is because CVI is C-based and not C++. That is why a code module forces an exception to occur, TestStand will catch it because TestStand internal code supports exception handling. For example if a DLL call using the CVI or DLL adapter causes an access violation, TestStand catches the exception and returns an error as the status for the step call. So if you can force an exception, TestStand will catch it and handle it.
Thanks again for your quick response and interest. In my code module I have an ActiveX event call back which is called when the terminate button on the operator interface is clicked. The operator interface was turned into an ActiveX server using the LabWindows wizard. This allows my code module to monitor for the state change in a different thread while the other thread executes my code normally. This is a nice way to terminate the code module immediately the only problems is losing the ActiveX connection between TestStand the LabWindows ActiveX application it has started to run the code modules. That is why I was trying to determine if and how I could re-esatblish this connection between Test Steps. Somehow TestStand re-establis hes this connection between executions.
Tom - To recap what I think you are doing. You have stated that you created an ActiveX server operator interface using CVI. I assume that the callback for the button on the GUI triggers an activeX event. I would assume that the code module registers itself to receive the event. In your case the code module is running in an out of process called tscvirun.exe which is being debugged by an instance of the CVI ADE. The event from the server executes the callback in the code module using an RPC thread so this is not the same thread that the code module is using. By calling KillProcess you are abruptly terminating the tscvirun.exe that CVI is debugging. The TestStand adapters are not writting to try to recover from this se nario without the execution restarting because all the handles for the module that it calls in the execution are lost when the process goes away.
The problem in trying to stop a thread from executing while it executes code that you do not own is a difficult issue to tackle. The safe way to deal with this issue is to write the code module to monitor for termination or some state change and exit gracefully.
If you need a safe guard to do something more abrupt within a certain timeframe you could call TerminateThread. Keep in mind that when you call this, the process can become unstable because the process still continues to run. You should only call this if the normal termination process does not work.
Note: TestStand does have a escalation mechanism for terminating an execution and it is based on time limits. The configuration for this is on the Time Limits tab of the Station Options dialog box. We have an option for terminating a thread but it is off by default.
Your recap is excellent. The SDK ExitProcess() function is called in a different thread in our code module when the Terminate button on our ActiveX operator interface is clicked. This kill's the code module immediately and returns to TestStand with an ActiveX error which can be ignored. Then the Cleanup group can be run to reset the test equipment. At this point you must restart another execution to reset the ActiveX connection. This model works well if you are resetting and not trying to perform other steps once you return to TestStand. This implies our code module will always be run out of process. I tried the Time Limits idea you suggested but that still does not terminate the thread our code module is executing in. The RaiseException idea I also tried. I raised the exception in the main thread of the code module that was executing. This has the side effect of returning to the CVI debugger instead of TestStand which is not good. We are going to stick with the ExitProcess() method for the time being and see how it works in the long run. In the next version of TestStand it would be nice if this feature could be added. Also for your info due to TestStand's architecture it seems all code module's that are called using the CVI adapter must return from the main of the code module that was called in TestStand. This implies that if the main has several layer's of functions below it that it calls. Somehow these call's must recognize (poll) for the termination event from TestStand and return all the way up the call stack back to the original code module that was called by TestStand. Therefore all of the users code/function's must error check every line of code in the main to see if we want to return and every function must check every line of code to return up the stack to the main. This makes the code a lot less readable in main for example if(function == FAIL) return -1; instead of function();. The only alternative to this method that I have found is the ANSI "C" setjmp/longjmp command which will allow us at any point in our Test Code or sub function's to return cleanly back to TestStand. This is preferable to me because the code is more readable and functionally equivalent to returning up the call stack. Do you know of any other methods of doing this ? Our individual code modules may be up to 1000 lines and take anywhere from 1 - 10 minutes to complete with many nested function's being called from the main.
Tom - In your last posting you stated: "...all code module's that are called using the CVI adapter must return from the main of the code module that was called in TestStand." This is an issue with all code module adapters, and is a basic issue of the OS, not necessarily TestStand. If a thread gives up control to a code module by invoking it, there is no way to "tell" the module to return or stop unless there is a specific mechanism for the code module to "see" the signal to stop. Now, if the language of the code module supports preemptive termination of a thread, the invoker could tell the intepreter to "stop" executing the code module. That just does not exist with the languages that TestStand suppor ts which is most.
You are correct that C programming does not allow for easily and safely crawling back up the call stack. Basically the exception handling in C++ is one mechansim to address this. The C++ programmer designs their code to catch exceptions where specific cleanup is required, and then rethrows the exception up the line. It still requires the code to be designed correctly.
Scott, Once again thanks for the help and excellent commentary. Last question really which method of returning up the call stack is preferred setjmp/longjmp or error checking each line. I see them as functionally the same ... just a matter of which style a programmer would prefer ... Would NI recommend one method over another for TestStand code modules ?
Tom - NI does not have a recommendation. I am not aware of many developers that like setjmp/longjmp. I have not seen it used in any of the C code that I have worked with. I think that good error handling is always the best way to go for C. Good luck with your project...