Starting and Shutting Down TestStand from a .NET User Interface

TestStand 2019 Help

Edition Date: May 2019

Part Number: 370052AA-01

»View Product Info
Download Help (Windows Only)

When you initialize the user interface application, use the ApplicationWrapper.Run method in the NationalInstruments.TestStand.Utility namespace to load the main form for the application that contains the TestStand Application Manager and other TestStand UI controls. The Run method calls System.Windows.Forms.Application.Run, which handles all events, such as menu selections, control value changes, and ActiveX control events. The method also works around exceptions that can occur on shutdown.

Use the MainForm_Load event to initialize the TestStand UI controls and use the ApplicationMgr.Start method to initialize the TestStand engine and log in a user.

Typically, you click the Close box or execute the Exit command through a TestStand menu or button control to stop an application. When operations request the form to close, the form raises the Closing event, where you must call the ApplicationMgr.Shutdown method to initiate shutdown of TestStand. If the return value from the Shutdown method indicates that shutdown is not complete, you must cancel the Close event. When shutdown is complete, TestStand raises the ApplicationMgr.ExitApplication event, where you call the Close method on the form.

Refer to the simple user interface examples for C# and .NET to see how to properly start and shut down TestStand.

Additional Considerations for .NET Applications to Properly Shut Down TestStand

The TestStand engine shutdown process requires the proper release of all references to TestStand objects, such as variables, sequence files, and the TestStand engine itself. When using the TestStand engine in a .NET application, you must consider the following challenges:

  • TestStand reports object leaks—The .NET CLR uses a form of automatic memory management, which includes garbage collection to reclaim memory, that is no longer in use. When the last reference to a TestStand COM object is released, .NET places the object reference into garbage collection, and the object is finalized at a later time.

    If the TestStand engine performs a shutdown before garbage collection finalizes TestStand objects, and you have the TestStand debug option Report Object Leaks enabled, TestStand reports non-finalized objects as leaks, even though the application no longer holds a reference to the objects.
  • TestStand engine cannot shut down—Some global objects of the .NET framework can retain references to TestStand objects that are passed to events of TestStand controls on Windows forms. If the application exits before releasing these references, the TestStand engine cannot perform a shutdown and some necessary tasks, such as saving some TestStand files.

To work around these challenges, NI recommends designing a TestStand .NET application to use a separate application domain instead of the default application domain and to force garbage collection and disposal of the TestStand engine.

A TestStand .NET application should perform the following startup and shutdown tasks:

  1. Create the TestStand engine in the default application domain.
  2. Create a new application domain and perform the following operations in that domain:
    • Create the TestStand engine to obtain a reference to the existing engine created by the default application domain.
    • Perform any startup operations on the engine object.
    • Perform operations that use the engine object, including loading and running the main form for the application.
    • Close any forms loaded into the application domain.
    • Release all TestStand objects, including the engine, created in the application domain.
    • Unload the application domain to ensure that all TestStand object references are finalized.
  3. Force garbage collection of all object generations.
  4. Force the release and finalization of the TestStand Engine COM object created in the default application domain.

To perform many of the previously listed tasks, you can call the LaunchTestStandApplicationInNewDomain.LaunchProtected method in the NationalInstruments.TestStand.Utility assembly. The LaunchProtected method creates an instance of the TestStand engine, loads a new application domain, calls a delegate main entry point in the application domain, and, before returning, the method properly unloads the application domain, forces garbage collection, and finalizes the TestStand engine COM object.

To properly cleanup before it returns, the LaunchTestStandApplicationInNewDomain.LaunchProtected method uses TSHelper.DoSynchronousGCForCOMObjectDestruction to force garbage collection by calling System.GC.Collect and GC.WaitForPendingFinalizers twice and calling System.Runtime.InteropServices.Marshal.FinalReleaseComObject on the engine reference.

Note Note  Calling FinalReleaseComObject does not cause the underlying Runtime Callable Wrapper (RCW) object to be garbage collected, and any subsequent method calls on the RCW will cause an InvalidComObjectException to be thrown.

Refer to the source code file, <TestStand>\API\DotNET\Source\TSUtil\TSUtil.cs, for more information about the implementation of the LaunchTestStandApplicationInNewDomain class and the TSHelper class that it uses.

The following example code illustrates the recommended way to use the TestStand engine in .NET:

using NationalInstruments.TestStand.Utility;
using NationalInstruments.TestStand.Interop.API;

[STAThread]
static void Main(string[] args)
{

// Create application domain, call MainEntryPoint, and
// cleanup before return.
LaunchTestStandApplicationInNewDomain.LaunchProtected(
   new LaunchTestStandApplicationInNewDomain.
       MainEntryPointDelegateWithArgs(MainEntryPoint),
   args,
   "TestStand Application",
   new LaunchTestStandApplicationInNewDomain.
       DisplayErrorMessageDelegate(DisplayErrorMessage));

}

// Main entry point executed in new application domain.
private static void MainEntryPoint(string[] args)
{

// Obtain a reference to existing Engine.
Program._engine = new Engine();
Program._engine.LoadTypePaletteFilesEx(
   TypeConflictHandlerTypes.ConflictHandler_Prompt, 0);

// Launch your application here.
// e.g., Application.Run(new MainForm());

// Engine clean up.

Program._engine = null;

}

// Called if exception occurs in MainEntryPoint.
private static void DisplayErrorMessage(string caption, string message)
{

// Handle error here.
// e.g., MessageBox.Show(message, caption);

}

private static Engine _engine = null;

Refer to the simple user interface examples for C# and .NET to see how to use a separate application domain to properly shut down TestStand.

WAS THIS ARTICLE HELPFUL?

Not Helpful