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,057 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,011 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,005 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
(1,995 Views)
Solution
Accepted by 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
(1,987 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
(1,982 Views)
Solution
Accepted by 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
(1,965 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
(1,955 Views)