C#中重定向stdout和stderr到同一个文件(用WinAPI实现)
acmilan2016/07/04软件综合 IP:四川

C#自带的Process类只能重定向到管道,并且stdout和stderr只能分别接收,无法达到命令提示符的效果。这里实现了一个Cmd类,其中XXXXXXnCmd函数可以运行命令同时返回所有输出。

Cmd.cs:

<code class="language-cs">using System;
using System.Runtime.InteropServices;
using System.IO;
using System.Text;
using System.ComponentModel;
using System.Diagnostics;

/// <summary>
/// Summary description for Class1
/// </summary>
public class Cmd
{
	public Cmd()
	{
		//
		// TODO: Add constructor logic here
		//
	}

    [StructLayout(LayoutKind.Sequential)]
    private class SECURITY_ATTRIBUTES
    {
        public int nLength;
        public IntPtr lpSecurityDescriptor;
        public bool bInheritHandle;
    }

    [Flags]
    private enum STARTF : uint
    {
        STARTF_USESHOWWINDOW = 0x00000001,
        STARTF_USESIZE = 0x00000002,
        STARTF_USEPOSITION = 0x00000004,
        STARTF_USECOUNTCHARS = 0x00000008,
        STARTF_USEFILLATTRIBUTE = 0x00000010,
        STARTF_RUNFULLSCREEN = 0x00000020,  // ignored for non-x86 platforms
        STARTF_FORCEONFEEDBACK = 0x00000040,
        STARTF_FORCEOFFFEEDBACK = 0x00000080,
        STARTF_USESTDHANDLES = 0x00000100,
    }

    private const Int16 SW_HIDE = 0;

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private class STARTUPINFO
    {
        public Int32 cb;
        public string lpReserved;
        public string lpDesktop;
        public string lpTitle;
        public Int32 dwX;
        public Int32 dwY;
        public Int32 dwXSize;
        public Int32 dwYSize;
        public Int32 dwXCountChars;
        public Int32 dwYCountChars;
        public Int32 dwFillAttribute;
        public STARTF dwFlags;
        public Int16 wShowWindow;
        public Int16 cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;
    }

    [StructLayout(LayoutKind.Sequential)]
    private class PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public int dwProcessId;
        public int dwThreadId;
    }

    private const uint GENERIC_WRITE = 0x40000000;
    private const uint FILE_SHARE_READ = 1;
    private const uint OPEN_EXISTING = 3;
    private const uint FILE_ATTRIBUTE_NORMAL = 0x80;

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CreateFile(
         string filename,
         uint access,
         uint share,
         [In] SECURITY_ATTRIBUTES securityAttributes,
         uint creationDisposition,
         uint flagsAndAttributes,
         IntPtr templateFile);

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool CreateProcess(
       string lpApplicationName,
       string lpCommandLine,
       [In] SECURITY_ATTRIBUTES lpProcessAttributes,
       [In] SECURITY_ATTRIBUTES lpThreadAttributes,
       bool bInheritHandles,
       uint dwCreationFlags,
       IntPtr lpEnvironment,
       string lpCurrentDirectory,
       [In] STARTUPINFO lpStartupInfo,
       [Out] PROCESS_INFORMATION lpProcessInformation);

    private const uint INFINITE = 0xFFFFFFFF;
    private const uint WAIT_FAILED = 0xFFFFFFFF;

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);

    [DllImport("kernel32.dll", SetLastError = false)]
    private static extern IntPtr GetCurrentProcess();

    private const uint DUPLICATE_SAME_ACCESS = 2;
    
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool DuplicateHandle(
        IntPtr proc1, IntPtr handle1,
        IntPtr proc2, out IntPtr handle2,
        uint access, bool inheritable, uint flags);

    [DllImport("kernel32.dll", SetLastError = false)]
    private static extern bool CloseHandle(IntPtr handle);

    public static string RunCmd(string cmd)
    {
        string tmpname = Path.GetTempFileName();

        // WIN32 section starts

        SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
        sa.nLength = Marshal.SizeOf(sa);
        sa.bInheritHandle = true;
        sa.lpSecurityDescriptor = IntPtr.Zero;

        IntPtr outhandle = CreateFile(tmpname, GENERIC_WRITE, FILE_SHARE_READ, sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
        if (outhandle == new IntPtr(-1))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        IntPtr outhandle2 = new IntPtr(-1);
        if (!DuplicateHandle(GetCurrentProcess(), outhandle, GetCurrentProcess(), out outhandle2, 0, true, DUPLICATE_SAME_ACCESS))
        {
            CloseHandle(outhandle);
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        
        STARTUPINFO si = new STARTUPINFO();
        si.cb = Marshal.SizeOf(si);
        si.dwFlags = STARTF.STARTF_USESTDHANDLES | STARTF.STARTF_USESHOWWINDOW;
        si.hStdInput = new IntPtr(-1);
        si.hStdOutput = outhandle;
        si.hStdError = outhandle2;
        si.wShowWindow = SW_HIDE;

        PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

        if (!CreateProcess(null, cmd, null, null, true, 0, IntPtr.Zero, null, si, pi))
        {
            CloseHandle(outhandle);
            CloseHandle(outhandle2);
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        CloseHandle(pi.hThread);
        CloseHandle(outhandle);
        CloseHandle(outhandle2);

        if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
        {
            CloseHandle(pi.hProcess);
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        CloseHandle(pi.hProcess);
        
        // WIN32 section ends

        string outstr = File.ReadAllText(tmpname, Encoding.Default);

        File.Delete(tmpname);

        return outstr;
    }
}
</code>

[修改于 7年11个月前 - 2016/07/04 17:17:08]

来自:计算机科学 / 软件综合
3
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
acmilan 作者
7年11个月前 修改于 7年11个月前 IP:四川
822262

RunCmd过程分解:

  1. 用XXXXXXXtTempFileName()创建一个临时文件
  2. 用CreateFile打开它,其中第四个参数中SECURITY_ATTRIBUTES结构体中的bInheritHandle设置为true,表示句柄可继承
  3. 用DuplicateHandle复制一份句柄
  4. 设置STARTUPINFO。开启STARTF_USESTDHANDLES,将stdin设置为空文件句柄,将stdout和stderr分别设置为这两个句柄;开启STARTF_USESHOWWINDOW,将wShowWindow设置为SW_HIDE,隐藏窗口
  5. 用CreateProcess创建进程,第五个参数设置为true,表示继承句柄,最后一个参数返回PROCESS_INFORMATION结构体,包含有进程和主线程句柄
  6. 用CloseHandle关闭用不到的句柄
  7. 用WaitForSingleObject等待进程退出
  8. 用CloseHandle关闭进程句柄
  9. 用XXXXXXXadAllText读取临时文件
  10. 用XXXXXXXlete删除临时文件
  11. 返回读取到的字符串
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
acmilan作者
7年11个月前 修改于 7年11个月前 IP:四川
822269

C++/CLI版本(使用时需要#include "Cmd.h")

Cmd.cpp:

<code class="language-cpp">#include "StdAfx.h"
#include "Cmd.h"
</code>

Cmd.h:

<code class="language-cpp">#pragma once

#include <vcclr.h>
#include <windows.h>
#pragma comment(lib, "kernel32.lib")

using namespace System;
using namespace System::IO;
using namespace System::Text;
using namespace System::Runtime::InteropServices;
using namespace System::ComponentModel;

public ref class Cmd
{
public:
	Cmd()
	{
	}

	static String^ RunCmd(String^ cmd)
	{
		String ^tmpname = Path::GetTempFileName();
		pin_ptr<const wchar_t> p_tmpname = PtrToStringChars(tmpname);
		
		SECURITY_ATTRIBUTES sa = { sizeof sa };
		sa.bInheritHandle = TRUE;

		HANDLE outhandle = CreateFile(p_tmpname, GENERIC_WRITE, FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
		if (outhandle == INVALID_HANDLE_VALUE)
		{
			throw gcnew Win32Exception(GetLastError());
		}

		HANDLE outhandle2 = INVALID_HANDLE_VALUE;
		if (!DuplicateHandle(GetCurrentProcess(), outhandle, GetCurrentProcess(), &outhandle2, 0, TRUE, DUPLICATE_SAME_ACCESS))
		{
			DWORD errcode = GetLastError();
			CloseHandle(outhandle);
			throw gcnew Win32Exception(errcode);
		}

		WCHAR *p_cmd = (WCHAR *)Marshal::StringToHGlobalUni(cmd).ToPointer();

		STARTUPINFO si = { sizeof si };
		si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
		si.hStdInput = INVALID_HANDLE_VALUE;
		si.hStdOutput = outhandle;
		si.hStdError = outhandle2;
		si.wShowWindow = SW_HIDE;

		PROCESS_INFORMATION pi = { 0 };

		if (!CreateProcess(NULL, p_cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
		{
			DWORD errcode = GetLastError();
			CloseHandle(outhandle);
			CloseHandle(outhandle2);
			Marshal::FreeHGlobal(IntPtr(p_cmd));
			throw gcnew Win32Exception();
		}

		Marshal::FreeHGlobal(IntPtr(p_cmd));
		CloseHandle(pi.hThread);
		CloseHandle(outhandle);
		CloseHandle(outhandle2);

		if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
		{
			DWORD errcode = GetLastError();
			CloseHandle(pi.hProcess);
			throw gcnew Win32Exception(errcode);
		}

		CloseHandle(pi.hProcess);
		
		String ^outstr = File::ReadAllText(tmpname, Encoding::Default);

		File::Delete(tmpname);

		return outstr;
	}
};
</const></windows.h></vcclr.h></code>
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
acmilan作者
7年11个月前 修改于 7年11个月前 IP:四川
822450

C++/CLI第二版——重定向了stdin,并可指定编码格式

<code class="language-cpp">// CmdHelper.h

#pragma once

#include <vcclr.h>
#include <windows.h>
#pragma comment(lib, "kernel32.lib")

using namespace System;
using namespace System::IO;
using namespace System::Text;
using namespace System::Runtime::InteropServices;
using namespace System::ComponentModel;

namespace CmdHelper {

	public ref class Cmd
	{
	public:
		Cmd()
		{
		}

		static String^ RunCmd(String^ cmd, String^ instr, Encoding^ enc)
		{
			String ^tmpin = Path::GetTempFileName();
			pin_ptr<const wchar_t> p_tmpin = PtrToStringChars(tmpin);
			
			array<unsigned char> ^inbytes = enc->GetBytes(instr);
			File::WriteAllBytes(tmpin, inbytes);
	        
			String ^tmpname = Path::GetTempFileName();
			pin_ptr<const wchar_t> p_tmpname = PtrToStringChars(tmpname);
	        
			SECURITY_ATTRIBUTES sa = { sizeof sa };
			sa.bInheritHandle = TRUE;

			HANDLE inhandle = CreateFile(p_tmpin, GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
			if (inhandle == INVALID_HANDLE_VALUE)
			{
				throw gcnew Win32Exception(GetLastError());
			}

			HANDLE outhandle = CreateFile(p_tmpname, GENERIC_WRITE, FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
			if (outhandle == INVALID_HANDLE_VALUE)
			{
				DWORD errcode = GetLastError();
				CloseHandle(inhandle);
				throw gcnew Win32Exception(errcode);
			}

			HANDLE outhandle2 = INVALID_HANDLE_VALUE;
			if (!DuplicateHandle(GetCurrentProcess(), outhandle, GetCurrentProcess(), &outhandle2, 0, TRUE, DUPLICATE_SAME_ACCESS))
			{
				DWORD errcode = GetLastError();
				CloseHandle(inhandle);
				CloseHandle(outhandle);
				throw gcnew Win32Exception(errcode);
			}

			WCHAR *p_cmd = (WCHAR *)Marshal::StringToHGlobalUni(cmd).ToPointer();

			STARTUPINFO si = { sizeof si };
			si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
			si.hStdInput = inhandle;
			si.hStdOutput = outhandle;
			si.hStdError = outhandle2;
			si.wShowWindow = SW_HIDE;

			PROCESS_INFORMATION pi = { 0 };

			if (!CreateProcess(NULL, p_cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
			{
				DWORD errcode = GetLastError();
				CloseHandle(inhandle);
				CloseHandle(outhandle);
				CloseHandle(outhandle2);
				Marshal::FreeHGlobal(IntPtr(p_cmd));
				throw gcnew Win32Exception(errcode);
			}

			Marshal::FreeHGlobal(IntPtr(p_cmd));
			CloseHandle(pi.hThread);
			CloseHandle(inhandle);
			CloseHandle(outhandle);
			CloseHandle(outhandle2);

			if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
			{
				DWORD errcode = GetLastError();
				CloseHandle(pi.hProcess);
				throw gcnew Win32Exception(errcode);
			}

			CloseHandle(pi.hProcess);
	        
			array<unsigned char> ^outbytes = File::ReadAllBytes(tmpname);
			String ^outstr = enc->GetString(outbytes);

			File::Delete(tmpin);
			File::Delete(tmpname);

			return outstr;
		}
	};
}
</unsigned></const></unsigned></const></windows.h></vcclr.h></code>
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,5年3个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}