From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

NI TestStand

cancel
Showing results for 
Search instead for 
Did you mean: 

How to release components correctly via Api (especially Users.ini) in vb.net

Solved!
Go to solution

Dear all,

I wrote a small console application in vb.net which should:

  • connect to teststand Api,
  • check if user exist,
  • check if password match,
  • report via exit code which access level the user do have.

 

Problem now is: when I try to release the Engine on this step:

 

 

System.Runtime.InteropServices.Marshal.FinalReleaseComObject(tsEngine)

 

 

I get the following error message, that the components I used for user management are not released.

 

MAGL21_0-1635229069911.png

 

            

So my question do anyone know how to release this components correctly?

 

 

Fyi: I’ve used for programming this manual:

Starting and Shutting Down TestStand from a .NET User Interface - NI TestStand 2014 SP1 Help - Natio...

-> but it will not work if I do it exactly like this way, because I do get no Object reference 

0 Kudos
Message 1 of 8
(2,132 Views)

I have NULL experience with vb.net so... I will first try to assign NULL to the acquired engine 😄 If that doesn't help, I would try to get inspiration from this.

Michał Bieńkowski
CLA, CTA

Someone devote his time to help solve your problem? Appreciate it and give kudos. Problem solved? Accept as a solution so that others can find it faster in the future.
Make a contribution to the development of TestStand - vote on TestStand Idea Exchange.
0 Kudos
Message 2 of 8
(2,086 Views)

Hello Michał,

 

Thanks for your friendly feedback 😀 .

 

But unfortunately I still tried all of this,

 

my Cleanup looks like this: 

        Finally
            'Cleanup
            If tsEngine IsNot Nothing Then
                tsEngine.UnloadTypePaletteFiles()
                tsEngine.UnloadAllModules()
                tsEngine.ShutDown(True)
                TSHelper.DoSynchronousGCForCOMObjectDestruction()
                System.GC.Collect() : GC.WaitForPendingFinalizers()
                System.GC.Collect() : GC.WaitForPendingFinalizers()
                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(tsEngine)
                tsEngine = Nothing 'Nothing = NULL
            End If
        End Try

 

 

Problem Here is when I do only set Engine to NULL then the Teststand Api overwrites my exit-code with -1073740791.

Only when I do release the ComObject:

System.Runtime.InteropServices.Marshal.FinalReleaseComObject(tsEngine)

my application returns me the correct exit code.

 

I also tried to set the Engine to NULL before the Release but then I get a NULL -Reference exception

 

FYI: If someone has a solution in C#, it's also interesting form me.

 

Best Rgds 

MAGL

 

0 Kudos
Message 3 of 8
(2,080 Views)

I was experimenting quite a long time ago with C# based TS CLI. I don't remember what is what, but it was releasing resources properly. Have a look at it. Maybe it will give you some hints. It is based on the article I linked before.

 

    class CLI
    {
        private static Engine engine = null;
        private static SequenceFile sequence = null;
        private static SequenceFile model = null;

        static void Main(string[] args)
        {
            // Create application domain, call MainEntryPoint, and
            // cleanup before return.
            LaunchTestStandApplicationInNewDomain.LaunchProtected(
               new LaunchTestStandApplicationInNewDomain.MainEntryPointDelegateWithArgs(MainEntryPoint),
               args,
               "TestStand CLI",
               new LaunchTestStandApplicationInNewDomain.DisplayErrorMessageDelegate(DisplayErrorMessage));
        }

        // Main entry point executed in new application domain.
        private static void MainEntryPoint(string[] args)
        {
            // Obtain a reference to existing Engine.
            engine = new Engine();
            engine.UIMessagePollingEnabled = true;

            // To get rid of "Interop type cannot be embedded." error, open NationalInstruments.TestStand.Interop.API properties and set "Embed Interop Types" property to False.
            sequence = engine.GetSequenceFileEx(
                args.GetValue(0).ToString(), 
                GetSeqFileOptions.GetSeqFile_NoOptions, 
                TypeConflictHandlerTypes.ConflictHandler_Error);

            //model = sequence.GetModelSequenceFile(out string modelDesc);

            System.Console.WriteLine("Starting new execution (" + sequence.Path + ").");

            Execution execution = engine.NewExecution(sequence, "MainSequence", null, false, ExecutionTypeMask.ExecTypeMask_Normal);
            while (!execution.WaitForEndEx(100))
            {
                if (!engine.IsUIMessageQueueEmpty)
                {
                    UIMessage msg = engine.GetUIMessage();
                    System.Console.WriteLine(msg.Event);
                    if (msg.Event == UIMessageCodes.UIMsg_EndFileExecution)
                    {
                        SequenceFile seq = msg.ActiveXData as SequenceFile;
                        System.Console.WriteLine(seq.Path);
                        engine.ReleaseSequenceFileEx(
                            seq,
                            ReleaseSeqFileOptions.ReleaseSeqFile_DoNotRunUnloadCallback |
                            ReleaseSeqFileOptions.ReleaseSeqFile_UnloadFile);
                        seq = null;
                    }
                    msg.Acknowledge();
                    //msg = null;
                }
            }
            System.Console.WriteLine("Execution ended.");
            //System.Threading.Thread.Sleep(5000);
            for (int i = 0; i < 10; i++)
            {
                if (!engine.IsUIMessageQueueEmpty)
                {
                    UIMessage msg = engine.GetUIMessage();
                    System.Console.WriteLine(msg.Event);
                    if (msg.Event == UIMessageCodes.UIMsg_EndFileExecution)
                    {
                        SequenceFile seq = msg.ActiveXData as SequenceFile;
                        System.Console.WriteLine(seq.Path);
                        engine.ReleaseSequenceFileEx(
                            seq,
                            ReleaseSeqFileOptions.ReleaseSeqFile_DoNotRunUnloadCallback |
                            ReleaseSeqFileOptions.ReleaseSeqFile_UnloadFile);
                        seq = null;
                    }
                    msg.Acknowledge();
                }
                System.Threading.Thread.Sleep(1000);
            }
            System.Console.WriteLine("The END");

            //engine.ReleaseSequenceFileEx(
            //    sequence,
            //    ReleaseSeqFileOptions.ReleaseSeqFile_DoNotRunUnloadCallback | 
            //    ReleaseSeqFileOptions.ReleaseSeqFile_UnloadFile);
            //sequence = null;

            //engine.ReleaseSequenceFileEx(
            //    model,
            //    ReleaseSeqFileOptions.ReleaseSeqFile_DoNotRunUnloadCallback |
            //    ReleaseSeqFileOptions.ReleaseSeqFile_UnloadFile);
            //model = null;

            // Engine clean up.
            engine = null;
        }

        // Called if exception occurs in MainEntryPoint.
        private static void DisplayErrorMessage(string caption, string message)
        {
            System.Console.WriteLine("Error: " + caption + "\n" + message);
        }
  
    }

 

Michał Bieńkowski
CLA, CTA

Someone devote his time to help solve your problem? Appreciate it and give kudos. Problem solved? Accept as a solution so that others can find it faster in the future.
Make a contribution to the development of TestStand - vote on TestStand Idea Exchange.
0 Kudos
Message 4 of 8
(2,070 Views)
Solution
Accepted by topic author MAGL21

Have you tried something like this? I don't have the setup to try this, that is why I wrote "pseudo-code". I'm guessing that, in your case, you would just need to release the user object and engine.

 

    class CLI
    {
        private static Engine engine = null;

        static void Main(string[] args)
        {
            // Create application domain, call MainEntryPoint, and
            // cleanup before return.
            LaunchTestStandApplicationInNewDomain.LaunchProtected(
               new LaunchTestStandApplicationInNewDomain.MainEntryPointDelegateWithArgs(MainEntryPoint),
               args,
               "TestStand CLI",
               new LaunchTestStandApplicationInNewDomain.DisplayErrorMessageDelegate(DisplayErrorMessage));
        }

        // Main entry point executed in new application domain.
        private static void MainEntryPoint(string[] args)
        {
            // Obtain a reference to existing Engine.
            engine = new Engine();
			
			//HERE STARTS PSEUDO CODE
			
			USER = engine.GetUser(STRING loginName);
			IF USER == NULL 
				USER DO NOT EXIST
			ELSE
				PASSOK = USER.ValidatePassword(STRING passwordString);
				PRIVOK = USER.HasPrivilege(STRING privilegeName)
				USER = NULL
			
			//HERE ENDS PSEUDO CODE
			
            engine = null;
        }

        // Called if exception occurs in MainEntryPoint.
        private static void DisplayErrorMessage(string caption, string message)
        {
            System.Console.WriteLine("Error: " + caption + "\n" + message);
        }

    }

 

Michał Bieńkowski
CLA, CTA

Someone devote his time to help solve your problem? Appreciate it and give kudos. Problem solved? Accept as a solution so that others can find it faster in the future.
Make a contribution to the development of TestStand - vote on TestStand Idea Exchange.
0 Kudos
Message 5 of 8
(2,062 Views)

I think that you might need to use Dispose() method on User object. Engine should be disposed automatically. At least that how it would work in a sequence.

Michał Bieńkowski
CLA, CTA

Someone devote his time to help solve your problem? Appreciate it and give kudos. Problem solved? Accept as a solution so that others can find it faster in the future.
Make a contribution to the development of TestStand - vote on TestStand Idea Exchange.
0 Kudos
Message 6 of 8
(2,057 Views)
Solution
Accepted by topic author MAGL21

Hello Michał,

 

Thank you so much for your competent support😁.

with your help, I could finish the Program without error💪.

 

But I don't know exactly why it was before.

I tested so may conditions.... 🙈

 

For all who read this in future:

my Suggestions where the Problem was:

  • I don't declare user Object explicit and so I do not set it to NULL/Nothing 
  • I think I had a little trouble wit the lauchprotectet Method -> but now its ok 
  • and I use Teststand 64bit and in Visual Studio was a marker -> force 32bit active 

>> Whatever Its working 😉

 

Best Rgds and Thanks so much!

MAGL21

 

Here is my vb.net code

VB.net Code:

Imports NationalInstruments.TestStand.Interop.API
Imports NationalInstruments.TestStand.Utility
Module Module1

    Private tsEngine As Engine
    Private TsUser As User
    Public iExitcode As eExCd = 0
    'EnumExitCode
    Public Enum eExCd
        'General
        exitUndefinedError = 0
        exitUserNotExist = 1
        exitWrongPassword = 2
        'Wrong Pwd
        exitWrongPwdAsNothing = 10
        exitWrongPwdAsOperator = 11
        exitWrongPwdAsTechnician = 12
        exitWrongPwdAsDeveloper = 13
        exitWrongPwdAsAdministrator = 14
        'Logged In
        exitLoginAsNothing = 20
        exitLoginAsOperator = 21
        exitLoginAsTechnician = 22
        exitLoginAsDeveloper = 23
        exitLoginAsAdministrator = 24
        'Error
        exitErrorArguments = 40
        exitErrorConnectingAPI = 41
    End Enum



    Sub Main(args() As String)
        Dim objEntryPoint As New LaunchTestStandApplicationInNewDomain.MainEntryPointDelegateWithArgsReturnCode(AddressOf MainEntryPoint)
        Dim objErrorMsg As New LaunchTestStandApplicationInNewDomain.DisplayErrorMessageDelegate(AddressOf DisplayErrorMessage)
        Environment.Exit(LaunchTestStandApplicationInNewDomain.LaunchProtectedReturnCode(objEntryPoint, args, "TestStand CLI", objErrorMsg))

    End Sub
    Sub OnClose(sender As Object, e As EventArgs)
        DebugConsole("Exitcode: " & iExitcode & " (" & [Enum].GetName(GetType(eExCd), iExitcode) & ")")
        DebugConsole(vbCrLf & "********************************************************************************************************")
    End Sub



    'Main Entry Point
    ''' <summary>
    ''' Becomes called by Main Fct -> TestStand Engine in new Domain
    ''' </summary>
    ''' <param name="args"></param>
    ''' <returns></returns>
    Private Function MainEntryPoint(args() As String) As Integer
        '------------------------------------------------------------------------------------------------------------
        Dim bDebugMode As Boolean = True                                                   'Aktivate DebugMode Here
        If bDebugMode Then ReDim args(1) : args(0) = "" : args(1) = ""                     'Debug Mode (User & Pwd)
        '------------------------------------------------------------------------------------------------------------
        AddHandler AppDomain.CurrentDomain.ProcessExit, AddressOf OnClose

        DebugConsole(vbCrLf & "********************************************************************************************************")
        If args.Length <> 0 Then
            Dim iFbk As Int32 = Fct_CheckUser(args(0), args(1))
            iExitcode = iFbk
        Else
            DebugConsole("Error40: Arguments >> Program requires exactly two Arguments (User, Password)")
            iExitcode = eExCd.exitErrorArguments
        End If
        Return iExitcode
    End Function


    'Fct_CheckUser
    ''' <summary>
    ''' Checks User Settings:
    ''' Exist, Password & Acceslevel 
    ''' </summary>
    ''' <param name="sUser"></param>
    ''' <param name="sPwd"></param>
    ''' <returns></returns>
    Public Function Fct_CheckUser(sUser As String, sPwd As String) As Int32
        Dim sUserLevel As String = ""
        Fct_CheckUser = eExCd.exitUndefinedError
        sUser = sUser.ToLower

        Try
            'Connection to Teststand 
            Dim tsEngine As New Engine

            'Check User Login
            DebugConsole("Check User: " & sUser & vbCrLf)
            Dim objUser = tsEngine.GetUser("sUser")
            If objUser IsNot Nothing Then

                Dim sLoginName As String = objUser.LoginName
                Dim sFullName As String = objUser.FullName
                'GetUserLevel
                Fct_CheckUser = eExCd.exitWrongPwdAsNothing : sUserLevel = "Nothing(Exit x0) "
                If Fct_GroupMember(sUser, "Operator", tsEngine) Then Fct_CheckUser = eExCd.exitWrongPwdAsOperator : sUserLevel = "Operator(Exit x1)"
                If Fct_GroupMember(sUser, "Technician", tsEngine) Then Fct_CheckUser = eExCd.exitWrongPwdAsTechnician : sUserLevel = "Technician(Exit x2)"
                If Fct_GroupMember(sUser, "Developer", tsEngine) Then Fct_CheckUser = eExCd.exitWrongPwdAsDeveloper : sUserLevel = "Developer(Exit x3)"
                If Fct_GroupMember(sUser, "Administrator", tsEngine) Then Fct_CheckUser = eExCd.exitWrongPwdAsAdministrator : sUserLevel = "Administrator(Exit x4)"
                'Password
                If objUser.ValidatePassword(sPwd) Then
                    Fct_CheckUser += 10
                    DebugConsole("User login successful : " & vbCrLf & sFullName & "[" & sLoginName & "]; Level: " & sUserLevel & vbCrLf)
                Else DebugConsole("User login denied! - incorrect password: " & vbCrLf & sFullName & "[" & sLoginName & "]; Level: " & sUserLevel & vbCrLf)
                End If

                'Cleanup - User
                objUser = Nothing
            Else
                DebugConsole("Exit1: User do not exist!")
                Fct_CheckUser = eExCd.exitUserNotExist
            End If

        Catch ex As Exception
            DebugConsole("Error41: Problem connecting to API; Exception: " & ex.Message)
            Fct_CheckUser = eExCd.exitErrorConnectingAPI
        End Try

        'Cleanup
        TsUser = Nothing
        TsEngine = Nothing
    End Function


    'Fct_MembersGroup
    ''' <summary>
    ''' Seach if User (sUser) is member of Usergroup (sPrivilegeGroup) from NI Teststand
    ''' </summary>
    ''' <param name="sUser"></param>
    ''' <param name="sPrivilegeGroup"></param>
    ''' <returns></returns>
    Private Function Fct_GroupMember(sUser As String, sPrivilegeGroup As String, ByRef tmpEngine As Engine) As Boolean
        Fct_GroupMember = False
        Try
            Dim aTempString(1) As String
            If tmpEngine.GetUserGroup(sPrivilegeGroup).AsPropertyObject.GetValVariant("Members", 0).GetType = aTempString.GetType Then
                Dim aTempString2 = tmpEngine.GetUserGroup(sPrivilegeGroup).AsPropertyObject.GetValVariant("Members", 0)
                Dim iCtr As Integer
                For iCtr = 0 To aTempString2.GetUpperbound(0)
                    If aTempString2(iCtr).ToUpper = sUser.ToUpper Then
                        Return True
                    End If
                Next
            End If
        Catch
            Return False
        End Try
    End Function

    'DisplayErrorMessage
    ''' <summary>
    ''' Handels Error
    ''' </summary>
    ''' <param name="caption"></param>
    ''' <param name="message"></param>
    Private Sub DisplayErrorMessage(caption As String, message As String)
        System.Console.WriteLine("Error: " + vbCrLf + caption + vbCrLf + message)
    End Sub

    'DebugConsole
    ''' <summary>
    ''' Console Output and DebugPrint combined
    ''' </summary>
    ''' <param name="sMsg"></param>
    Private Sub DebugConsole(sMsg As String)
        Console.Write(sMsg & vbCrLf)
        Debug.Print(sMsg)
    End Sub

End Module

 

Message 7 of 8
(2,040 Views)

Awesome! Thanks for your VB code. Maybe I will finally learn something 😄

Michał Bieńkowski
CLA, CTA

Someone devote his time to help solve your problem? Appreciate it and give kudos. Problem solved? Accept as a solution so that others can find it faster in the future.
Make a contribution to the development of TestStand - vote on TestStand Idea Exchange.
0 Kudos
Message 8 of 8
(2,030 Views)