4

我的用例如下:

在 Visual Studio 中,在调试时我启动 powershell 并提供一个脚本,连同几个动作,它启动项目可执行文件,如下所示:

&$exeToStart $exeParams | Out-Host

当我从 powershell 控制台运行脚本时,这很有效。当我通过 VS 停止图标停止调试时,我的问题就开始了,这基本上会杀死运行此脚本的 powershell 进程,从而留下一个挂起的 exe 进程,之后我必须手动杀死该进程。

所以,我的问题是如何确保当visual studio杀死父powershell进程时,子进程也被它杀死?

4

3 回答 3

3

我怀疑 Visual Studio 是否有办法在您停止调试时终止所有子进程。

一种解决方案是运行 PowerShell 脚本进行清理。您可以使用 Win32_Process CimInstance 上的 ParentProcessId 属性。它可能看起来像(使用 PowerShell V3):

 Get-CimInstance win32_process |
    ? { (Get-Process -id $_.ParentProcessId -ea Ignore) -eq $null } |
    Stop-Process -WhatIf

这将返回父进程不再存在的进程列表。请注意,要稳健地使用它,您需要过滤您知道自己启动的进程。例如,如果我试图杀死上述命令返回的所有进程,我会尝试杀死 csrss.exe、wininit.exe 和其他我不想被杀死的进程。

您甚至可以创建一个宏以在 Visual Studio 中运行以停止调试并运行您的 PowerShell 脚本。包控制台使用 PowerShell,请参阅http://www.wintellect.com/blogs/jrobbins/using-nuget-powershell-to-replace-missing-macros-in-dev-11了解这可能是什么样的一些想法。

于 2013-09-24T20:34:41.903 回答
3

你会在这里找到做“工作”的东西。下面的代码是概念证明。您必须将它放在一个函数中并在所有嵌入脚本的开头调用它。

#TestJob.ps1
$CreateJobObjectSignature = @"
using System;
using System.Text;
using System.Runtime.InteropServices;
public class ClsNativ
{
  public enum JOBOBJECTINFOCLASS
  {
    AssociateCompletionPortInformation = 7,
    BasicLimitInformation = 2,
    BasicUIRestrictions = 4,
    EndOfJobTimeInformation = 6,
    ExtendedLimitInformation = 9,
    SecurityLimitInformation = 5,
    GroupInformation = 11
  }

  [StructLayout(LayoutKind.Sequential)]
  struct JOBOBJECT_BASIC_LIMIT_INFORMATION
  {
    public Int64 PerProcessUserTimeLimit;
    public Int64 PerJobUserTimeLimit;
    public UInt32 LimitFlags;
    public UIntPtr MinimumWorkingSetSize;
    public UIntPtr MaximumWorkingSetSize;
    public UInt32 ActiveProcessLimit;
    public Int64 Affinity;
    public UInt32 PriorityClass;
    public UInt32 SchedulingClass;
  }


  [StructLayout(LayoutKind.Sequential)]
  struct IO_COUNTERS
  {
    public UInt64 ReadOperationCount;
    public UInt64 WriteOperationCount;
    public UInt64 OtherOperationCount;
    public UInt64 ReadTransferCount;
    public UInt64 WriteTransferCount;
    public UInt64 OtherTransferCount;
  }

  [StructLayout(LayoutKind.Sequential)]
  struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
  {
    public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
    public IO_COUNTERS IoInfo;
    public UIntPtr ProcessMemoryLimit;
    public UIntPtr JobMemoryLimit;
    public UIntPtr PeakProcessMemoryUsed;
    public UIntPtr PeakJobMemoryUsed;
  }

  [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
  public static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string lpName);

  [DllImport("kernel32.dll")]
  public static extern bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess);

  [DllImport("kernel32.dll")]
  public static extern bool SetInformationJobObject(IntPtr hJob, JOBOBJECTINFOCLASS JobObjectInfoClass, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);

  [DllImport("kernel32.dll")]
  public static extern IntPtr GetCurrentProcess();


  private const UInt32 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000;
  public ClsNativ()
  {
    IntPtr hJob = CreateJobObject(IntPtr.Zero, "JobName");

    JOBOBJECT_BASIC_LIMIT_INFORMATION info = new JOBOBJECT_BASIC_LIMIT_INFORMATION();
    info.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;

    JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
    extendedInfo.BasicLimitInformation = info;

    int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
    IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);
    Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);

    SetInformationJobObject(hJob, JOBOBJECTINFOCLASS.ExtendedLimitInformation, extendedInfoPtr, (uint)length);

    IntPtr hProcess = GetCurrentProcess();
    bool blRc = AssignProcessToJobObject(hJob, hProcess);

    Marshal.FreeHGlobal(extendedInfoPtr);
  }
}
"@



Add-Type -TypeDefinition $CreateJobObjectSignature 
$a = New-Object ClsNativ

& notepad.exe
Start-Process -FilePath "c:\windows\system32\calc.exe"

Read-Host "Push return key !"

概念:我创建一个作业,一个系统对象,而不是一个计划作业,也不是一个 PowerShell 作业。我用“JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE”配置它,这样如果作业的最后一个句柄被关闭,那么里面的所有进程都被关闭。然后我将 PowerShell 进程分配给该作业。所有子进程都属于该作业,并且由于唯一的作业句柄位于 PowerShell 进程内,因此当这个进程死亡时,其他所有子进程都会消失。所以它成功了。

我在带有x64 PowerShell的7上对其进行了测试。问题是无法将作业添加到已经属于作业的进程中(VS 2010正常启动的情况下)

我在带有x64 PowerShell的Windows 8上对其进行了测试。

这是测试它的方法:

powershell -file "C:\temp\TestJob.ps1"

使用 Windows 7,当您正常启动 Visual Studio 时,它已经在作业中,如下面的截图所示:

在此处输入图像描述

但是,如果您像这样使用 WMI 启动 Visual Studio 2010:

Invoke-WmiMethod -path win32_process -name create -argumentlist "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe"

它不再属于 Job :

在此处输入图像描述

因此,当您在调试子进程时启动 PowerShell 时,当 PowerShell 停止时,它们都将停止:

在此处输入图像描述

于 2013-09-24T21:14:21.367 回答
0

Powershell脚本:

Get-CimInstance win32_process -Filter "Name like 'myprocess.exe'" | ? { (Get-Process -id $_.ParentProcessId -ea Ignore) -eq $null } | Select-Object ProcessId | ? { Stop-Process $_.ProcessId -Force }

这是在阅读 Jason 的答案后对我有用的 Powershell 脚本。

于 2015-05-27T14:37:48.333 回答