C#在窗体程序中运行控制台程序并管理其进程

运行环境

  • .Net Framework-4.7.1
  • visual studio 2017

一. 控制台程序的运行

  • 使用Process类,官方文档地址

  • 使用样例:

public void FrpStart()
{
    if (p != null)
    {
        MessageBox.Show("进程已存在");
        return;
    }
    p = new Process
    {
        // Configure the process using the StartInfo properties.
        StartInfo =
            {
            	//调用的程序名称,比如windows下的cmd,linux下的sh或者bash,即这里要填写控制台程序的路径
                FileName = Utils.GetTempPath() + "/frpc.exe",
                //参数,MainConfig为配置文件路径
                Arguments = "-c " + MainConfig,
                //控制台程序所在的路径
                WorkingDirectory = Utils.GetTempPath(),
                WindowStyle = ProcessWindowStyle.Hidden,
                UseShellExecute = false,
                CreateNoWindow = true,
                //重定向输入输出
                RedirectStandardInput = true,
                RedirectStandardOutput = true
            }
    };
    //监听控制台的输出
    p.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
    {
        // Prepend line numbers to each line of the output.
        if (!string.IsNullOrEmpty(e.Data))
        {
            append(e.Data);
        }
    });
    p.Start();
    p.BeginOutputReadLine();
}

注意上面新建进程的参数UseShellExecute = false,如果这里设置为false,那么FileName这个参数中控制台程序的只能用绝对路径,即WorkingDirectory参数无效。
如果不设置UseShellExecute为false,则无法重定向输出。
如果UseShellExecute = true,则FileName可以直接使用控制台程序的名字,前提是WorkingDirectory里面的路径是正确的。


二. 进程管理

接下来就是关于控制台进程的管理了,如果仅仅按照上面编写,程序关闭之后控制台程序会残留在进程中。
由于无法将控制台程序当成线程运行,因此需要一个东西用来将控制台进程与主程序关联在一起。
这里参考了这篇帖子

  • 继续以上面的程序为例
public void FrpStart()
{
	//检测是否存在残留的线程,并将其关闭
    Process[] existingPrivoxy = Process.GetProcessesByName("frpc");
    foreach (Process p in existingPrivoxy)
    {
        KillProcess(p);
    }
	..........
    p.Start();
    p.BeginOutputReadLine();
    //将其加入Job
    //Job的初始化省略了,可以在构造函数初始化,使用单例模式
    Job.AddProcess(p.Handle);
}

private static void KillProcess(Process p)
{
    try
    {
        p.CloseMainWindow();
        p.WaitForExit(100);
        if (!p.HasExited)
        {
            p.Kill();
            p.WaitForExit();
        }
    }
    catch (Exception e)
    {
    }
}
  • 以上即为线程的启动以及终止,最后就是Job的实现,全部都是借鉴网上的教程的,对win32的api不熟悉,这里就不多赘述。使用该类,当主进程退出是,子进程也会退出。

public class Job : IDisposable
{
    private IntPtr handle = IntPtr.Zero;

    public Job()
    {
        handle = CreateJobObject(IntPtr.Zero, null);
        var extendedInfoPtr = IntPtr.Zero;
        var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
        {
            LimitFlags = 0x2000
        };

        var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
        {
            BasicLimitInformation = info
        };

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

            if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
                    (uint)length))
                throw new Exception(string.Format("Unable to set information.  Error: {0}",
                    Marshal.GetLastWin32Error()));
        }
        finally
        {
            if (extendedInfoPtr != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(extendedInfoPtr);
                extendedInfoPtr = IntPtr.Zero;
            }
        }
    }

    public bool AddProcess(IntPtr processHandle)
    {
        var succ = AssignProcessToJobObject(handle, processHandle);

        if (!succ)
        {
            Console.WriteLine("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
        }

        return succ;
    }

    public bool AddProcess(int processId)
    {
        return AddProcess(Process.GetProcessById(processId).Handle);
    }

    #region IDisposable

    private bool disposed;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposed) return;
        disposed = true;

        if (disposing)
        {
            // no managed objects to free
        }

        if (handle != IntPtr.Zero)
        {
            CloseHandle(handle);
            handle = IntPtr.Zero;
        }
    }

    ~Job()
    {
        Dispose(false);
    }

    #endregion

    #region Interop

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

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CloseHandle(IntPtr hObject);

    #endregion
}

#region Helper classes

[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_BASIC_LIMIT_INFORMATION
{
    public Int64 PerProcessUserTimeLimit;
    public Int64 PerJobUserTimeLimit;
    public UInt32 LimitFlags;
    public UIntPtr MinimumWorkingSetSize;
    public UIntPtr MaximumWorkingSetSize;
    public UInt32 ActiveProcessLimit;
    public UIntPtr Affinity;
    public UInt32 PriorityClass;
    public UInt32 SchedulingClass;
}

[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
    public UInt32 nLength;
    public IntPtr lpSecurityDescriptor;
    public Int32 bInheritHandle;
}

[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;
}

public enum JobObjectInfoType
{
    AssociateCompletionPortInformation = 7,
    BasicLimitInformation = 2,
    BasicUIRestrictions = 4,
    EndOfJobTimeInformation = 6,
    ExtendedLimitInformation = 9,
    SecurityLimitInformation = 5,
    GroupInformation = 11
}

#endregion

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值