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

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

"hello world" | out-gridview

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

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

【问题讨论】:

    标签: gridview powershell


    【解决方案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
        

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-01-31
          • 2019-08-26
          • 1970-01-01
          • 2021-06-05
          • 2016-08-08
          • 2020-04-11
          相关资源
          最近更新 更多