NI TestStand

cancel
Showing results for 
Search instead for 
Did you mean: 

COM calls to TestComplete software

Hi,

When we try to make COM calls to TestComplete software, it takes several attempts to create the object before it succeeds (meaning terminate the sequence file and re-execute the sequence).  The TestStand error message is "can't create object..application busy".

Contacted the TestComplete vendor and they troubleshooted the issue with Teststand trial version for few weeks, here's their conclusion:

"The root cause of the problem is not the slowdown but the fact that TestStand does not handle COM calls correctly. The slowdown just exposes the problem. The COM technology implies a communication protocol between clients and servers. This protocol allows long start of a COM server, this is a normal situation. The error in question is used to handle such situations and COM clients are supposed to repeat their calls later when this error occurs. It does not indicate that something is wrong with the COM server (TestComplete), it just indicates that it has not yet started entirely. So, the fact that TestStand handles this error as an exception in the COM object is an incorrect behavior of TestStand (which does not happen for JScript scripting engine, which handles COM correctly)"

My question is how to slow down the COM calls to inside TestStand ? And if the COM call fails, can we repeat the COM call?

Thanks,

ph2

 

0 Kudos
Message 1 of 9
(4,473 Views)

Are you making these COM calls within the TestStand Sequence Editor or are you calling the TestStand Engine?

Michael Bilyk
Former NI Software Engineer (IT)
0 Kudos
Message 2 of 9
(4,430 Views)

This is done within the main sequence file. It's a single step of Adapter type ActiveX/COM.  Thanks.

0 Kudos
Message 3 of 9
(4,418 Views)

Can you place it in a loop and do some error checking? Basically, check to see if the step fails, then restart the step using flow control steps? For instance, a do while.

Michael Bilyk
Former NI Software Engineer (IT)
0 Kudos
Message 4 of 9
(4,401 Views)

Yes, I tried that, put in a loop.  The ActiveX adapter doesn't pass or fail a step.  It just concludes with "Done".  Thanks.

0 Kudos
Message 5 of 9
(4,341 Views)

How do you check to see if the COM call fails? Is it only after the test or something?

Michael Bilyk
Former NI Software Engineer (IT)
0 Kudos
Message 6 of 9
(4,336 Views)

when the COM call step fails, a popup window will display the error message( saying application is busy). And the sequence file terminates and report generated.

0 Kudos
Message 7 of 9
(4,331 Views)

You can register an IOleMessageFilter using CoRegisterMessageFilter. Here is a .NET implementation. You should be able to do something similar in C/C++ if needed. What it does is automatically do the retries seamlessly to the client (assuming you are hitting the problem I think you are).

 

Requirements:

1) You will need to call your code in an STA thread (TestStand uses MTA threads by default, but you can call a sequence in a new thread and specify to use an STA thread in the advanced options and wait for the sequence/thread to complete using a wait step after the sequence call).

2) You need to call the Register method from your STA thread before making any calls to your COM server, and you should call Revoke after all calls are done (likely in the cleanup of your STA sequence).

 

Another, possible solution would be to write your own wrapper code around the COM server that does the retries and call your wrapper from TestStand instead of calling the COM server directly, or alternatively, you could do the retries at the teststand level by looping on the teststand steps (though you might want to disable result collection for those steps to avoid leaking memory from the results).

 

 

    public class OleMessageFilter : IOleMessageFilter
    {
        //
        // Class containing the IOleMessageFilter
        // thread error-handling functions.

        private IOleMessageFilter mOldFilter = null;

        // Start the filter.
        public void Register()
        {
            int returnVal = CoRegisterMessageFilter(this, out mOldFilter);
            System.Diagnostics.Debug.Assert(returnVal == 0/*S_OK*/);
        }

        // Done with the filter, close it.
        public void Revoke()
        {
            IOleMessageFilter notused = null;
            CoRegisterMessageFilter(mOldFilter, out notused);
        }

        //
        // IOleMessageFilter functions.
        // Handle incoming thread requests.
        int IOleMessageFilter.HandleInComingCall(int dwCallType,
          System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr
          lpInterfaceInfo)
        {
            //Return the flag SERVERCALL_ISHANDLED.
            return 0;
        }

        // Thread call was rejected, so try again.
        int IOleMessageFilter.RetryRejectedCall(System.IntPtr
          hTaskCallee, int dwTickCount, int dwRejectType)
        {
            if (dwRejectType == 2)
            // flag = SERVERCALL_RETRYLATER.
            {
                // Retry the thread call immediately if return >=0 &
                // <100.
                return 99;
            }
            // Too busy; cancel call.
            return -1;
        }

        int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee,
          int dwTickCount, int dwPendingType)
        {
            //Return the flag PENDINGMSG_WAITDEFPROCESS.
            return 2;
        }

        // Implement the IOleMessageFilter interface.
        [DllImport("Ole32.dll")]
        private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
    }

    [ComImport(), Guid("00000016-0000-0000-C000-000000000046"),
    InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    interface IOleMessageFilter
    {
        [PreserveSig]
        int HandleInComingCall(
            int dwCallType,
            IntPtr hTaskCaller,
            int dwTickCount,
            IntPtr lpInterfaceInfo);

        [PreserveSig]
        int RetryRejectedCall(
            IntPtr hTaskCallee,
            int dwTickCount,
            int dwRejectType);

        [PreserveSig]
        int MessagePending(
            IntPtr hTaskCallee,
            int dwTickCount,
            int dwPendingType);
    }

 

Hope this helps,

-Doug

0 Kudos
Message 8 of 9
(4,190 Views)

Hi Doug,

 

This is great info.  I will definitely give this a try.  Thanks so much!

 

Regards,

 

ph2

0 Kudos
Message 9 of 9
(4,174 Views)