0

I have the following code to create a combo box that shows a list of ports in use on the PC What I want to do is take the users choice of port and assign it to a system variable using the SETX command. At the moment the user enters the port number manually in a batch file, the aim is to skip this step and replace it with the choice from the combo box

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

    myPort = IO.Ports.SerialPort.GetPortNames()
    ComboBox1.Items.AddRange(myPort)

The batch file looks like this at present and works fine

    set /p port= "What is your port number  "
    setx port "%port%"

So is there a way to remove the user having to enter the port number and have the batch file run its script using the choice from the combo box Thanks

Tu deschizi eu inchid
  • 4,117
  • 3
  • 13
  • 24
Jud Slater
  • 21
  • 4
  • 1
    Could probably have a look at [ProcessStartInfo](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo?view=net-7.0) to run your batch file via your app with arguments. And maybe my understanding of SETX is a little off, but doesn't that just update the registry? If so, you can do that directly and skip the batch file – Hursey Jan 24 '23 at 19:32
  • the batch file is a little complicated to explain (ive fallen on that sword before) but the user needs to see the batch file as it runs – Jud Slater Jan 27 '23 at 17:35

2 Answers2

1

According to this post

set modifies the current shell's (the window's) environment values, and the change is available immediately, but it is temporary. The change will not affect other shells that are running, and as soon as you close the shell, the new value is lost until such time as you run set again.

setx modifies the value permanently, which affects all future shells, but does not modify the environment of the shells already running. You have to exit the shell and reopen it before the change will be available, but the value will remain modified until you change it again.


For set /p port= "What is your port number: "

one can use the following in VB.NET:

Environment.SetEnvironmentVariable("port", "COM5", EnvironmentVariableTarget.Process)

where "COM5" is your port number (replace this with the value from your ComboBox).


For setx port "%port%":

Permanent environment variables are stored in the registry: HKEY_CURRENT_USER\Environment.

To set an environment variable in HKEY_CURRENT_USER\Environment, do one of the following.

Option 1:

'by specifying 'EnvironmentVariableTarget.User', the value will be updated in the registry (HKEY_CURRENT_USER\Environment)
Environment.SetEnvironmentVariable("port", "COM5", EnvironmentVariableTarget.User)

where "COM5" is your port number (replace this with the value from your ComboBox).

Option 2:

Add Imports

  • Imports Microsoft.Win32

SetUserEnvironmentVarReg:

Private Sub SetUserEnvironmentVarReg(name As String, val As String, Optional regView As RegistryView = RegistryView.Registry64)
    Using currentUserKey As RegistryKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, regView)
        Using envKey As RegistryKey = currentUserKey.OpenSubKey("Environment", True)
            If envKey IsNot Nothing Then
                'set value
                envKey.SetValue(name, val)
            End If
        End Using
    End Using
End Sub

Usage:

SetUserEnvironmentVarReg("port", "COM5")

where "COM5" is your port number (replace this with the value from your ComboBox).


Let's create a test batch script. The test batch script will allow one command-line argument which will allow it to be used with the different options shown below. If a command-line argument is supplied, the batch script will used the specified value, otherwise it will use the value inherited from the environment variable.

TestEnvVar.bat:

@echo off

if not "%1" equ "" (
    set port=%1
)

echo port: %port%

One can use System.Diagnostics.Process to run the batch script.

RunProcessInheritEnvironmentVar:

Private Sub RunProcessInheritEnvironmentVar(filename As String, portName As String)

    'set environment variable (process-level)
    Environment.SetEnvironmentVariable("port", portName, EnvironmentVariableTarget.Process)

    'set environment variable (user-level)
    'sets environment variable in HKEY_CURRENT_USER\Environment
    Environment.SetEnvironmentVariable("port", portName, EnvironmentVariableTarget.User)

    Dim startInfo As ProcessStartInfo = New ProcessStartInfo(filename) With {.CreateNoWindow = True, .RedirectStandardError = True, .RedirectStandardOutput = True, .UseShellExecute = False, .WindowStyle = ProcessWindowStyle.Hidden}

    Using p As Process = New Process() With {.EnableRaisingEvents = True, .StartInfo = startInfo}

        AddHandler p.ErrorDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
                                            If Not String.IsNullOrEmpty(e.Data) Then
                                                'ToDo: add desired code
                                                Debug.WriteLine("error: " & e.Data)
                                            End If
                                        End Sub

        AddHandler p.OutputDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
                                             If Not String.IsNullOrEmpty(e.Data) Then
                                                 'ToDo: add desired code
                                                 Debug.WriteLine("output: " & e.Data)
                                             End If
                                         End Sub

        p.Start()

        p.BeginErrorReadLine()
        p.BeginOutputReadLine()

        'wait for exit
        p.WaitForExit()

    End Using
End Sub

Usage:

RunProcessInheritEnvironmentVar("C:\Temp\TestEnvVar.bat", "COM5")

where "COM5" is your port number (replace this with the value from your ComboBox).


It's not clear whether or not you actually need to set the environment variable permanently (ie: in HKEY_CURRENT_USER\Environment). If the environment variable is only being used for the batch script being executed, one may consider one of the alternate methods shown below.

When using System.Diagnostics.Process an environment variable can be set using ProcessStartInfo.EnvironmentVariables which sets the environment variable within the scope of the System.Diagnostics.Process process. The code below doesn't change the value in the registry.

RunProcessSetEnvironmentVar :

Private Sub RunProcessSetEnvironmentVar(filename As String, portName As String)
    Dim startInfo As ProcessStartInfo = New ProcessStartInfo(filename) With {.CreateNoWindow = True, .RedirectStandardError = True, .RedirectStandardOutput = True, .UseShellExecute = False, .WindowStyle = ProcessWindowStyle.Hidden}

    'add environment variable
    startInfo.Environment.Add("port", portName)

    Using p As Process = New Process() With {.EnableRaisingEvents = True, .StartInfo = startInfo}

        AddHandler p.ErrorDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
                                            If Not String.IsNullOrEmpty(e.Data) Then
                                                'ToDo: add desired code
                                                Debug.WriteLine("error: " & e.Data)
                                            End If
                                        End Sub

        AddHandler p.OutputDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
                                             If Not String.IsNullOrEmpty(e.Data) Then
                                                 'ToDo: add desired code
                                                 Debug.WriteLine("output: " & e.Data)
                                             End If
                                         End Sub


        p.Start()

        p.BeginErrorReadLine()
        p.BeginOutputReadLine()

        'wait for exit
        p.WaitForExit()

    End Using
End Sub

Usage:

RunProcessSetEnvironmentVar("C:\Temp\TestEnvVar.bat", "COM5")

where "COM5" is your port number (replace this with the value from your ComboBox).


Lastly, let's look at how one could specify the port name as an argument because the test batch script allows command-line arguments. The code below doesn't change the value in the registry.

RunProcessWithArgument:

Private Sub RunProcessWithArgument(filename As String, Optional arguments As String = Nothing)
    Dim startInfo As ProcessStartInfo = New ProcessStartInfo(filename) With {.CreateNoWindow = True, .RedirectStandardError = True, .RedirectStandardOutput = True, .UseShellExecute = False, .WindowStyle = ProcessWindowStyle.Hidden}

    If Not String.IsNullOrEmpty(arguments) Then
        startInfo.Arguments = arguments
    End If

    Using p As Process = New Process() With {.EnableRaisingEvents = True, .StartInfo = startInfo}

        AddHandler p.ErrorDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
                                            If Not String.IsNullOrEmpty(e.Data) Then
                                                'ToDo: add desired code
                                                Debug.WriteLine("error: " & e.Data)
                                            End If
                                        End Sub

        AddHandler p.OutputDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
                                             If Not String.IsNullOrEmpty(e.Data) Then
                                                 'ToDo: add desired code
                                                 Debug.WriteLine("output: " & e.Data)
                                             End If
                                         End Sub


        p.Start()

        p.BeginErrorReadLine()
        p.BeginOutputReadLine()

        'wait for exit
        p.WaitForExit()

    End Using
End Sub

Usage

RunProcessWithArgument("C:\Temp\TestEnvVar.bat", "COM5")

where "COM5" is your port number (replace this with the value from your ComboBox).


I've shown a variety of methods that can be used to specify an environment variable that can be used for a batch script depending upon your needs. For a slight variation of System.Diagnostics.Process usage see here.


Update:

When using System.Diagnostics.Process, one can also use StandardInput to provide value(s) for prompt(s).

TestEnvVar.bat:

@echo off

set /p port= "What is your port number:  "
setx port "%port%"

echo port: %port%

for /f "delims=" %%a in ('^(reg query "HKCU\Environment" /v "port" ^|find /i "port"^)') do (
  echo %%a
)

RunProcessWithStandardInput:

Private Sub RunProcessWithStandardInput(filename As String, portName As String)
    If String.IsNullOrEmpty(filename) Then
        Throw New Exception("Error: 'filename' is null or empty.")
    ElseIf Not System.IO.File.Exists(filename) Then
        Throw New Exception($"Error: '{filename}' could not be found.")
    End If

    If String.IsNullOrEmpty(portName) Then
        Throw New Exception("Error: 'portName' is null or empty.")
    End If

    Dim startInfo As ProcessStartInfo = New ProcessStartInfo(filename) With {.CreateNoWindow = True, .RedirectStandardError = True, .RedirectStandardInput = True, .RedirectStandardOutput = True, .UseShellExecute = False, .WindowStyle = ProcessWindowStyle.Hidden}

    Using p As Process = New Process() With {.EnableRaisingEvents = True, .StartInfo = startInfo}

        AddHandler p.ErrorDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
                                            If Not String.IsNullOrEmpty(e.Data) Then
                                                'ToDo: add desired code
                                                Debug.WriteLine("error: " & e.Data)
                                            End If
                                        End Sub

        AddHandler p.OutputDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
                                             If Not String.IsNullOrEmpty(e.Data) Then
                                                 'ToDo: add desired code
                                                 Debug.WriteLine("output: " & e.Data)
                                             End If
                                         End Sub


        p.Start()

        p.BeginErrorReadLine()
        p.BeginOutputReadLine()

        Using sw As System.IO.StreamWriter = p.StandardInput
            'provide values for each input prompt
            'ToDo: add values for each input prompt - changing the for loop as necessary
            'Note: Since we only have 1 prompt, using a loop Is unnecessary - a single 'WriteLine' statement would suffice

            'if there are additional prompts add them below; else if (i = 1)...
            For i As Integer = 0 To 1
                If i = 0 Then
                    'write port name to StandardInput
                    sw.WriteLine(portName)
                End If
            Next
        End Using

        'wait for exit
        p.WaitForExit()
    End Using
End Sub

Usage:

RunProcessWithStandardInput("C:\Temp\TestEnvVar.bat", "COM5")

where "COM5" is your port number (replace this with the value from your ComboBox).

Additional Resources:

Tu deschizi eu inchid
  • 4,117
  • 3
  • 13
  • 24
  • wow so much info , thats very much appreciated, I will now go through your response, it may take me a while as im new to this. – Jud Slater Jan 27 '23 at 17:33
  • All of the `RunProcess...` methods are mostly the same code - there are only a few lines that are different. I included the full code so that one can just cut and paste it. – Tu deschizi eu inchid Jan 27 '23 at 17:40
  • just had a good look at the above, I think ( I may be wrong) that you misunderstood the question as you keep saying replace this with the value from your ComboBox, if the combo box is dropped down and port X is chosen I need this to be the value set using setX, as the port value will always differ from user to user, or am i reading the code wrong, also the batch file removes the value once it finishes – Jud Slater Jan 28 '23 at 14:49
  • The code you've provided is _minimal_, but unfortunately isn't a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). It's not clear whether or not you've implemented the code that was provided in your other post. The code in the OP will require the serial port device to be connected prior to starting the app, and if a different serial port device is plugged in, the app will have to be closed and restarted. I consider this to be undesirable behavior. `ComboBox1` is a default name for a `ComboBox`. This should be renamed to a more meaningful name. – Tu deschizi eu inchid Jan 28 '23 at 19:49
  • _if the combo box is dropped down and port X is chosen I need this to be the value set_: This is why I've included the following: _replace this with the value from your ComboBox_. The following may be helpful: [ComboBox.Text](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.combobox.text?view=windowsdesktop-7.0), [ComboBox.SelectedText](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.combobox.selectedtext?view=windowsdesktop-7.0). If implementing the code provided in your other post, you'd use `ComboBox.SelectedValue`. – Tu deschizi eu inchid Jan 28 '23 at 19:58
  • When looking at the documentation for [ComboBox Class](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.combobox?view=windowsdesktop-7.0) one sees `public class ComboBox : System.Windows.Forms.ListControl` which means that `ComboBox` inherits from `ListControl` which is where the `SelectedValue` documentation can be found: [ListControl.SelectedValue](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.listcontrol.selectedvalue?view=windowsdesktop-7.0) – Tu deschizi eu inchid Jan 28 '23 at 20:01
  • For example, the actual usage may be: `RunProcessWithStandardInput("C:\Temp\TestEnvVar.bat", ComboBoxComPorts.SelectedValue.ToString())` – Tu deschizi eu inchid Jan 28 '23 at 20:09
  • Or it may be: `RunProcessWithStandardInput("C:\Temp\TestEnvVar.bat", ComboBoxComPorts.SelectedText)` – Tu deschizi eu inchid Jan 28 '23 at 20:11
  • Or maybe even: `RunProcessWithStandardInput("C:\Temp\TestEnvVar.bat", ComboBoxComPorts.Text)`, – Tu deschizi eu inchid Jan 28 '23 at 20:13
  • Of course this means that the `ComboBox` is named `ComboBoxComPorts`. – Tu deschizi eu inchid Jan 28 '23 at 20:14
  • Now that is really helpful, again many thanks for the input, I will spend sometime this week taking a look at the above – Jud Slater Jan 30 '23 at 17:55
  • Hi thanks for the help, but i realize now that I am just no good at this, ive tried a lot of google work but i cant work out how to take the selected value of the combo box and set it as an environment variable, i thought it would just be a simple line of code such as environmentvariable, setenvironmentvariable=selectedvalue. thanks again – Jud Slater Feb 11 '23 at 14:27
  • Start with the code that I posted in your [other post](https://stackoverflow.com/a/74895268/10024425). Option 2, in the other post, seems to have a little better performance. – Tu deschizi eu inchid Feb 11 '23 at 14:59
  • Then add the code above. If you're script prompts for the COM port name (ie: prompts for input), use the code under "Update" which should work with the batch script provided in the OP. If you'd like to change your batch script to not prompt for input, you can take advantage of 1 of the other methods. All of the code I've provided has been tested. I recommend that you use the code I provided verbatim (ie: don't try to adapt it for your usage). Once you've verified it's working, then adapt it. – Tu deschizi eu inchid Feb 11 '23 at 14:59
  • I'm not sure how you debug your code, but it can be as simple as using [System.Diagnostics.Debug.WriteLine](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.debug.writeline?view=net-7.0). Then when running the debugger, ensure that you click the "Output" tab (bottom-right section in Visual Studio when the debugger is running). – Tu deschizi eu inchid Feb 11 '23 at 15:13
  • Also ensure that you've enabled [Option Strict](https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/option-strict-statement#to-set-option-strict-in-the-ide). – Tu deschizi eu inchid Feb 11 '23 at 15:13
  • After combining the code I've provided (as mentioned above), if you still have issues, create a new post with all of the code that you're chosen to use and I'll try to assist you in getting it to work - ensure that you include a batch script for testing. Also be specific about what kind of project you've created, version of .NET or .NET Framework you're using, and version of Visual Studio that you're using. – Tu deschizi eu inchid Feb 11 '23 at 15:25
  • For the `ComboBox`, I set the `DropDownStyle` property to `DropDownList`. – Tu deschizi eu inchid Feb 11 '23 at 15:41
  • If you don't have VS 2022, you can download it [here](https://visualstudio.microsoft.com/downloads/). – Tu deschizi eu inchid Feb 11 '23 at 15:52
  • If all you want to do is set an environment variable, you may (or may not) have success with the following: `Environment.SetEnvironmentVariable("port", ComboBoxComPorts.SelectedValue.ToString(), EnvironmentVariableTarget.Process)` and `Environment.SetEnvironmentVariable("port", ComboBoxComPorts.SelectedValue.ToString(), EnvironmentVariableTarget.User)`. If it doesn't work you may try changing `ComboBoxComPorts.SelectedValue.ToString()` to `ComboBoxComPorts.Text`. – Tu deschizi eu inchid Feb 12 '23 at 13:25
  • Sorry been a bit busy with work, thanks again I will have a play with the above code and get back to you – Jud Slater Feb 21 '23 at 18:59
  • I decided to try the above environment.setenvironment section, I had issues with a null exception being thrown when using it, unless i use the combobox.text which doesnt show an error, but I cant find the variable in windows environments, I did follow the instructions you posted to create a new project but had errors with the project regarding the load form part, at this point I want to thank you for sticking with me, im old and VB is tough I will post a new question with all the info you requested – Jud Slater Feb 21 '23 at 20:27
  • Being as specific as possible will be helpful for whoever attempts to answer your question as well as for you. Some of the "complexity" in the answers is due to lack of information. For this question, as well as your previous one, it's important to know the operating system that you're using. Also, the version of Visual Studio that you're using, type of project that you're creating, whether your using .NET or .NET Framework (as well as the version). Lastly, include a batch scritpt that one can use for testing. – Tu deschizi eu inchid Feb 21 '23 at 21:05
  • _I did follow the instructions you posted to create a new project but had errors with the project regarding the load form part_: Are you talking about the instructions from the other post? A `null-reference` occurs when one accesses something whose value is `null` (or `Nothing`) - in essence, when one hasn't created a "new" instance of the object (Class, ComboBox, TextBox, etc...). For example, `Dim cb As ComboBox`. To create a new instance, one would use `Dim cb As New ComboBox` or `Dim cb As ComboBox = New ComboBox()` – Tu deschizi eu inchid Feb 21 '23 at 21:16
0

Well after much hard work and back and forth I finally figured out how to achieve my goal, I would like to thank @User09938 for sticking with me and for offering all the advice, is was just down to me to interpret it as a VB newbie and make it work for me.
I followed this guide see what is attached to a com port and show in combo box

and added the statement

Environment.SetEnvironmentVariable("port", ComboBoxComPorts.SelectedValue.ToString(), EnvironmentVariableTarget.Process) 

and

Environment.SetEnvironmentVariable("port", ComboBoxComPorts.SelectedValue.ToString(), EnvironmentVariableTarget.User)

Which worked.

To those who are adventuring into VB for the first time I have to say that this site is so very helpful

avariant
  • 2,234
  • 5
  • 25
  • 33
Jud Slater
  • 21
  • 4