开发者

How to prevent from closing out-gridview when a script finishes

开发者 https://www.devze.com 2022-12-17 17:23 出处:网络
I have a script that presents results using out-gridview. Here is a simple example: \"hello world\" | out-gridview

I have a script that presents results using out-gridview. Here is a simple example:

"hello world" | out-gridview

When I run the script using 开发者_开发问答Run with PowerShell, it will open gridview and close it immediately after it is opened. (I think this is because gridview is not modal and the script finishes.)

How to make the PowerShell wait until the gridview is manually closed?


How about:

"hello world" | out-gridview -wait

According to the documentation of Out-GridView, the option -Wait prevents Windows PowerShell from closing until the Out-GridView window is closed.

It is available in Windows PowerShell 3.0.


Just add -PassThru to the Out-Gridview - adds an "OK" and "Cancel" button to the gridview - now you can "run with powershell" and close the gridview window using the OK button when you are done - soooo much more straightforward!


You can either run Powershell.exe with -noexit or try this:

"hello world" | out-gridview
Read-Host "press enter to exit"

UPDATED: Out-GridView is non-blocking so if you want to test for it to exit you have to resort to some low-level Win32 APIs. The following code works in ISE (haven't tested it in the console host). Also it has a limitation - it basically looks for any window associated with the host process other than the host process's main window to go away. At that point, it will return. Turns out that Out-GridView isn't a child of the main window and its caption isn't consistent (GPS | Out-GridView or GPS | ogv or GPS | <any alias you make up>):

$src = @'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

namespace Utils
{
    public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);

    public class WindowHelper 
    {
        private const int PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
        private IntPtr _mainHwnd;
        private IntPtr _ogvHwnd;
        private IntPtr _poshProcessHandle;
        private int _poshPid;
        private bool _ogvWindowFound;

        public WindowHelper()
        {
            Process process = Process.GetCurrentProcess();
            _mainHwnd = process.MainWindowHandle;
            _poshProcessHandle = process.Handle;
            _poshPid = process.Id;
        }

        public void WaitForOutGridViewWindowToClose()
        {
            do 
            {
                _ogvWindowFound = false;
                EnumChildWindows(IntPtr.Zero, EnumChildWindowsHandler,
                                 IntPtr.Zero);
                Thread.Sleep(500);
            } while (_ogvWindowFound);
        }

        [DllImport("User32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool EnumChildWindows(
            IntPtr parentHandle, Win32Callback callback, IntPtr lParam);

        [DllImport("Oleacc.dll")]
        public static extern IntPtr GetProcessHandleFromHwnd(IntPtr hwnd);

        [DllImport("Kernel32.dll")]
        public static extern int GetProcessId(IntPtr handle);

        [DllImport("Kernel32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool DuplicateHandle(
            IntPtr hSourceProcessHandle, 
            IntPtr hSourceHandle, 
            IntPtr hTargetProcessHandle,
            out IntPtr lpTargetHandle,
            int dwDesiredAccess,
            bool bInheritHandle,
            int dwOptions);

        [DllImport("Kernel32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CloseHandle(IntPtr handle);

        [DllImport("Kernel32.dll")]
        public static extern int GetLastError();

        private bool EnumChildWindowsHandler(IntPtr hwnd, IntPtr lParam)
        {
            if (_ogvHwnd == IntPtr.Zero)
            {
                IntPtr hProcess = GetProcessHandleFromHwnd(hwnd);
                IntPtr hProcessDup;
                if (!DuplicateHandle(hProcess, hProcess, _poshProcessHandle,
                                     out hProcessDup, 
                                     PROCESS_QUERY_LIMITED_INFORMATION,
                                     false, 0))
                {
                    Console.WriteLine("Dup process handle {0:X8} error: {1}",
                                      hProcess.ToInt32(), GetLastError());
                    return true;
                }
                int processId = GetProcessId(hProcessDup);
                if (processId == 0)
                {
                    Console.WriteLine("GetProcessId error:{0}",
                                      GetLastError());
                    return true;
                }
                if (processId == _poshPid)
                {
                    if (hwnd != _mainHwnd)
                    {
                        _ogvHwnd = hwnd;
                        _ogvWindowFound = true;
                        CloseHandle(hProcessDup);
                        return false;
                    }
                }
                CloseHandle(hProcessDup);
            }
            else if (hwnd == _ogvHwnd)
            {
                _ogvWindowFound = true;
                return false;
            }
            return true;
        }
    }
}
'@

Add-Type -TypeDefinition $src

Get-Process | Out-GridView

$helper = new-object Utils.WindowHelper
$helper.WaitForOutGridViewWindowToClose()

"Done!!!!"


Keith's solution doesn't work for me for some reason. When I run it, I get errors, and the process terminates without waiting for the grid to close. I get output like this:

...
GetProcessId error:6
Dup process handle 0000015C error: 6
GetProcessId error:6
GetProcessId error:6
Dup process handle 00000148 error: 6
GetProcessId error:6
GetProcessId error:6
...

I don't really understand the Windows API's used in Keith's solution very well. But after some digging, I did find a way to modify the solution that is simpler. It involves fewer API calls and imports, and it doesn't produce any errors either. This is what I came up with.

$src = @'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

namespace Utils
{
  public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);

  public class WindowHelper
  {
    private const int PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
    private IntPtr _mainHwnd;
    private IntPtr _ogvHwnd;
    private IntPtr _poshProcessHandle;
    private int _poshPid;
    private bool _ogvWindowFound;

    public WindowHelper()
    {
      Process process = Process.GetCurrentProcess();
      _mainHwnd = process.MainWindowHandle;
      _poshProcessHandle = process.Handle;
      _poshPid = process.Id;
    }

    public void WaitForOutGridViewWindowToClose()
    {
      do
      {
        _ogvWindowFound = false;
        EnumChildWindows(IntPtr.Zero, EnumChildWindowsHandler,
                 IntPtr.Zero);
        Thread.Sleep(500);
      } while (_ogvWindowFound);
    }

    [DllImport("User32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool EnumChildWindows(
      IntPtr parentHandle, Win32Callback callback, IntPtr lParam);

    [DllImport("User32.dll")]
    public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int processId);

    [DllImport("Kernel32.dll")]
    public static extern int GetLastError();

    private bool EnumChildWindowsHandler(IntPtr hwnd, IntPtr lParam)
    {
      if (_ogvHwnd == IntPtr.Zero)
      {
        int processId;
        int thread = GetWindowThreadProcessId(hwnd, out processId);

        if (processId == 0)
        {
          Console.WriteLine("GetWindowThreadProcessId error:{0}",
                   GetLastError());
          return true;
        }
        if (processId == _poshPid)
        {
          if (hwnd != _mainHwnd)
          {
            _ogvHwnd = hwnd;
            _ogvWindowFound = true;
            return false;
          }
        }
      }
      else if (hwnd == _ogvHwnd)
      {
        _ogvWindowFound = true;
        return false;
      }
      return true;
    }
  }
}
'@

Add-Type -TypeDefinition $src

Get-Process | Out-GridView       
$helper = new-object Utils.WindowHelper 
$helper.WaitForOutGridViewWindowToClose()

Hopefully this will save someone else from the same issues I had.


This will keep the window open:

$DataResults  | Out-GridView -Title $Title -PassThru | clip

This allows you to display object list (for example) with a title and when you click OK it saves the data to the clipboard.

0

精彩评论

暂无评论...
验证码 换一张
取 消