脚本完成时如何防止关闭 out-gridview

Posted

技术标签:

【中文标题】脚本完成时如何防止关闭 out-gridview【英文标题】:How to prevent from closing out-gridview when a script finishes 【发布时间】:2011-01-07 01:28:38 【问题描述】:

我有一个使用out-gridview 显示结果的脚本。这是一个简单的例子:

"hello world" | out-gridview

当我使用 Run with PowerShell 运行脚本时,它会打开 gridview 并在打开后立即关闭它。 (我认为这是因为 gridview 不是模态的,脚本完成了。)

如何让PowerShell等到gridview手动关闭?

【问题讨论】:

【参考方案1】:

怎么样:

"hello world" | out-gridview -wait

根据the documentation of Out-GridView,选项-Wait 会阻止Windows PowerShell 关闭,直到Out-GridView 窗口关闭。

它在 Windows PowerShell 3.0 中可用。

【讨论】:

【参考方案2】:

只需将-PassThru 添加到 Out-Gridview - 将“确定”和“取消”按钮添加到 gridview - 现在您可以“使用 powershell 运行”并在完成后使用 OK 按钮关闭 gridview 窗口 -太简单了!

【讨论】:

这仅在 PowerShell 3.0 之后可用,它还引入了-Wait【参考方案3】:

您可以使用-noexit 运行 Powershell.exe 或试试这个:

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

更新: Out-GridView 是非阻塞的,所以如果你想测试它是否退出,你必须求助于一些低级别的 Win32 API。以下代码在 ISE 中有效(尚未在控制台主机中对其进行测试)。它也有一个限制——它基本上会寻找与主机进程关联的任何窗口,而不是主机进程的主窗口。到那时,它会返回。事实证明 Out-GridView 不是主窗口的子窗口,并且其标题不一致(GPS | Out-GridView 或 GPS | ogv 或 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!!!!"

【讨论】:

任何更复杂的解决方案可以找到打开的窗口并等待它们关闭? 我修复了最初发布 C# 源代码时出现的两个问题,随后将其格式化以适应不需要滚动条。 不会使用 -title 指定 GridView 窗口的标题来简化查找过程吗?【参考方案4】:

由于某种原因,Keith 的解决方案对我不起作用。当我运行它时,我得到了错误,并且该过程终止而无需等待网格关闭。我得到这样的输出:

...
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
...

我不太了解 Keith 的解决方案中使用的 Windows API。但是经过一番挖掘,我确实找到了一种修改更简单的解决方案的方法。它涉及更少的 API 调用和导入,也不会产生任何错误。这就是我想出的。

$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()

希望这可以帮助其他人摆脱与我相同的问题。

【讨论】:

【参考方案5】:

这将使窗口保持打开状态:

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

这允许您显示带有标题的对象列表(例如),当您单击“确定”时,它会将数据保存到剪贴板。

【讨论】:

以上是关于脚本完成时如何防止关闭 out-gridview的主要内容,如果未能解决你的问题,请参考以下文章

单击背景时如何防止laravel jetstream模式关闭?

防止/禁止 macOS 在 shell 脚本运行时关闭

如何防止excel关闭复制选框

如何防止slideToggle在第一次点击时打开然后关闭

Swift:如何防止未显示的 UITableViewCells 在滚动时被关闭

程序完成后如何防止控制台关闭?