Force Dialog (File Chooser) box to Front

This topic contains 5 replies, has 3 voices, and was last updated by  James Bernie 1 week, 1 day ago.

  • Author
    Posts
  • #104086

    Eric Queck
    Participant

    Hello Everyone!

    I am currently working with a script that will allow users to select a file using a dialog box. This is the start of a script that will import the CSV that they select and process the data.

    The issue that I am running into is that when I use VS Code to run this function, the dialog box always stays behind VS Code until I manually activate the window. If I run this in Powershell ISE it will pop up to the front of the screen like I would expect. Does anyone know of a way to pull the form to the front when I am using VS Code?

    Function Get-FileName($initialDirectory)
    {   
     [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
     Out-Null
     
     $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog 
     $OpenFileDialog.initialDirectory = $initialDirectory
     $OpenFileDialog.filter = "CSV (*.csv)| *.csv"
     $OpenFileDialog.ShowDialog() | Out-Null
     $OpenFileDialog.filename
     $OpenFileDialog.ShowHelp = $true
    } #end function Get-FileName
    
  • #104105

    postanote
    Participant

    As for...

    If I run this in Powershell ISE it will pop up to the front of the screen like I would expect.

    It should, because these are two different apps that are being ran. The ISE is of course PowerShell_ise.exe, the console window in the ISE is not a real ConsoleHost. When you run code in the ISE, it actually spawn a PowerShell.exe instance to run it and that is separate from the powershell_ise.exe instance all the while returning the output back to the ISE console. Hence why it can come to the front.

    In VSCode, the code is run as a child process. Hence not coming front. You can see this in Get-Process in both tools.

    When you do this in the ISE, you will see a single powershell_ise.exe process.
    When you run the code in the ISE, and do Get-Process again, you will see a new powershell.exe process.

    When you run VSCode, you will see several powershell.exe processes. One for the start of VSCode (default ConsolHost) and the other for when you start the Editor which triggers the 'PowerShell Integrated Console – Visual Studio Code Host'.

    # VSCode

     
    Get-Process -Name '*powershell*'
    
    Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
    -------  ------    -----      -----     ------     --  -- -----------
        590      28    83808      93452       1.09     32  26 powershell
        728      43   140340     139116       3.86   6840  26 powershell
    

    If you open TaskManager, you'll see only one VSC task, but expand that and you'll see double digit child processes, depending on what you loaded up.

    The VSCode console dropdown will show the number of powershell.exe process are running in the parent process. So, at the start and a blank editor / pane, there will be two. The moment you run this code in the Editor, it will spawn a third powershell.exe instance. If you open TaskManger you will see the VSC process until you expand it and see all the child processes.

     Get-Process -Name '*powershell*'
    
    Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
    -------  ------    -----      -----     ------     --  -- -----------
        584      28    83696      93408       1.19     32  26 powershell
        626      31    85092      95592       1.14   6544  26 powershell
       1246      71   328936     349968      16.95   6840  26 powershell
    

    Also, if you look at the bottom right of the VSC console, you will see the work PowerShell with a spinning icon to the left of it. This means that the parent editor (which itself seems to be model) is running the new PowerShell instance and in a model dialog box thus behind the parent. However, if you go back to TaskManager, you'll see a separate 'Windows PowerShell' task running.

    So, in order for you to bring this to the front, you have to minimize the VSCode window. You can do this in your code. Yet, this just means you have to click the taskbar icon (or ALT+TAB) to bring the VSCode window back up to work on your code again. Otherwise you are going to have to write additional code to try and grab that process and make it active. That is a bit more of a challenge, especially for model dialog boxes. Been there, done that, not successful.

    So, when I do this sort of thing like you are, I just minimize VSC...

        Function Get-FileName($initialDirectory)
        {   
            [system.reflection.assembly]::loadwithpartialname("System.Windows.Forms")
            [System.Windows.Forms.SendKeys]::Sendwait("% {n}")
    
            [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
            Out-Null
    
            $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog 
            $OpenFileDialog.initialDirectory = $initialDirectory
            $OpenFileDialog.filter = "CSV (*.csv)| *.csv"
            $OpenFileDialog.ShowDialog() | Out-Null
            $OpenFileDialog.filename
            $OpenFileDialog.ShowHelp = $true
        } #end function Get-FileName
    
        Get-FileName -initialDirectory D:\Temp
    

    … and work with my dialogs, and ALT+TAB to maximize VSC when done.

    Even using the above, sometimes it will minimize to show the dialog behind, other times it will bring the dialog to the front, depending on what other screens / dialogs I had up at the time. I have a 4 monitor setup with many items up at once.

    Otherwise, I write my code, save the file and shell-out, so as to not have to minimize VSC. Both at not the best experience, but it is what it is. Well, unless someone else tells me I am smoking something, that is causing my in ability to accomplish this. 8^}

    I also spent a good bit of time VSC editor settings and PowerShellEditorServices for a fix, no luck.

    • #104215

      Eric Queck
      Participant

      Thanks Postanote! That was a very detailed answer and after banging my head on this problem for probably another 2 hours after posting, I was heading down the same path you described. I started running the get process method to see what window I needed to grab and maximize, but nothing worked. I am in total agreement with you that minimizing VS Code will be the best option here and your additions to the script work perfectly!

      Thanks for taking the time and leaving such a detailed response!

    • #104237

      James Bernie
      Participant

      duplicate post

  • #104240

    James Bernie
    Participant

    duplicate post 2

  • #104243

    James Bernie
    Participant

    Question, do you really need to have the file selection be a prompt in a separate Window or would something within the PowerShell session be ok?

    As part of a much larger script I have a file selection where if there is more than one file found it then uses the code below to provide a list with a row number for each file so you would just enter the row number to select the file & continue to process the data within the CSV file.

    In my case I have to prompt for the file extension but as you are only looking at CSV files you can simplify things a bit. You will need to create a variable $FileList which stores your file list, in my case, initially I just return the counter, file Name and last write time but you can modify that to suit your needs. The "M" option for $FileRow just allows the use to exit out of the function in case they made a wrong selection.

    $Counter = 0
    $Range = 1..(($FileList | Measure).Count)
    $FileList | ForEach{
    $FileList[$Counter] = "$($Counter+1)." + " " + $FileList[$Counter].Name + "   " + $FileList[$Counter].LastWriteTime + "   " + $FileList[$Counter].Directory
    $Counter++
    }
            Write-Host "There are multiple files with the extension CSV:"
            $FileList
            Write-Host
                DO {$FileRow = Read-Host -Prompt 'Select the row # of the file you want to query? (M = Menu)'
                    } WHILE (($FileRow -notin $Range) -and ($FileRow -ne "M"))
    

    Remember, when you are working with arrays they start at [0] but the list will start at 1 (yay!) so when the user makes a selection in the background you need to -1 from the selection to ensure you get the actual file.

    $FileName = ($FileList[$FileRow -1]).Name
    

    It has been a while since I wrote that function, it does quite a lot more but hopefully this gives you another way to do things & I remembered all the quirks. 🙂

    (edit to remove backticks.. oops! & I keep making the post disappear some how 🙁 )

You must be logged in to reply to this topic.