10-26-2021 01:40 AM - last edited on 11-05-2024 10:45 AM by Content Cleaner
Dear all,
I wrote a small console application in vb.net which should:
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.
So my question do anyone know how to release this components correctly?
Fyi: I’ve used for programming this manual:
-> but it will not work if I do it exactly like this way, because I do get no Object reference
Solved! Go to Solution.
10-27-2021 04:44 PM
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.
10-28-2021 12:54 AM
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
10-28-2021 02:42 AM
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);
}
}
10-28-2021 01:11 PM
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);
}
}
10-28-2021 02:29 PM
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.
10-29-2021 03:00 AM
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:
>> 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
10-29-2021 03:08 AM
Awesome! Thanks for your VB code. Maybe I will finally learn something 😄