Windows服务程序访问NAS

由于服务程序授权与用户桌面授权互不影响,所以即使在用户桌面映射了NAS盘符,在Windows服务程序下仍然是不可以访问的。

一般来说有这样几种做法来访问NAS或共享盘:
1、加入域,通过“域用户认证”或“通过将NAS资源设置对某台计算机授权”来达到访问NAS的目的
2、不加域,在Windows中添加与NAS相同的用户名及密码,服务程序通过该用户名密码登录,可以访问NAS
3、不加域,通过调阅API的方式来获得访问NAS的权限
A、VC实现如下:

DWORD AcessNetDrtive(TCHAR* szRemotePath, TCHAR* szLocalDriver,  const TCHAR* szUsername, const TCHAR* szPassword)
{
	DWORD dwRetVal;
	DWORD dwFlags;

	NETRESOURCE nr;
	memset(&nr, 0, sizeof(NETRESOURCE));
	nr.dwScope = RESOURCE_GLOBALNET;
	nr.dwType = RESOURCETYPE_ANY;
	nr.lpLocalName = szLocalDriver;
	nr.lpRemoteName = szRemotePath;
	nr.lpProvider = NULL;

	//CONNECT_REDIRECT;CONNECT_UPDATE_PROFILE;
	dwFlags = 0;
	dwRetVal = WNetAddConnection2(&nr, szPassword, szUsername, dwFlags);

	retrun dwRetVal;
}

B、C#实现如下:

    public class NetworkDrive
    {
        public enum ResourceScope
        {
            RESOURCE_CONNECTED = 1,
            RESOURCE_GLOBALNET,
            RESOURCE_REMEMBERED,
            RESOURCE_RECENT,
            RESOURCE_CONTEXT
        }

        public enum ResourceType
        {
            RESOURCETYPE_ANY,
            RESOURCETYPE_DISK,
            RESOURCETYPE_PRINT,
            RESOURCETYPE_RESERVED
        }

        public enum ResourceUsage
        {
            RESOURCEUSAGE_CONNECTABLE = 0x00000001,
            RESOURCEUSAGE_CONTAINER = 0x00000002,
            RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
            RESOURCEUSAGE_SIBLING = 0x00000008,
            RESOURCEUSAGE_ATTACHED = 0x00000010,
            RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
        }

        public enum ResourceDisplayType
        {
            RESOURCEDISPLAYTYPE_GENERIC,
            RESOURCEDISPLAYTYPE_DOMAIN,
            RESOURCEDISPLAYTYPE_SERVER,
            RESOURCEDISPLAYTYPE_SHARE,
            RESOURCEDISPLAYTYPE_FILE,
            RESOURCEDISPLAYTYPE_GROUP,
            RESOURCEDISPLAYTYPE_NETWORK,
            RESOURCEDISPLAYTYPE_ROOT,
            RESOURCEDISPLAYTYPE_SHAREADMIN,
            RESOURCEDISPLAYTYPE_DIRECTORY,
            RESOURCEDISPLAYTYPE_TREE,
            RESOURCEDISPLAYTYPE_NDSCONTAINER
        }

        [StructLayout(LayoutKind.Sequential)]
        private class NETRESOURCE
        {
            public ResourceScope dwScope = 0;
            public ResourceType dwType = 0;
            public ResourceDisplayType dwDisplayType = 0;
            public ResourceUsage dwUsage = 0;
            public string lpLocalName = null;
            public string lpRemoteName = null;
            public string lpComment = null;
            public string lpProvider = null;
        }

        [DllImport("mpr.dll")]
        private static extern int WNetAddConnection2(NETRESOURCE lpNetResource, string lpPassword, string lpUsername, int dwFlags);

        public static int MapNetworkDrive(string remotePath, string localDrive, string userName, string passWord)
        {
            NETRESOURCE myNetResource = new NETRESOURCE();
            myNetResource.lpLocalName = localDrive;
            myNetResource.lpRemoteName = remotePath;
            myNetResource.lpProvider = null;
            int result = WNetAddConnection2(myNetResource, passWord, userName, 0);
            return result;
        }
    }

IIS程序访问NAS

IIS程序访问NAS,一般有两种情况,加域和没加域
1、加域后,什么都好办,可以通过两种方式认证
A、采用域帐户认证
B、NAS资源可以直接授权给指定计算机访问

2、不加域,那一般有三种方式设置访问权限
A、通过指定程序的用户名和密码访问(这里指定的不是本地的用户名和密码,而是NAS的用户名和密码)
B、不指定用户名和密码,直接通过pass-through authentication方式访问,其规则为:
首先通过用户的真实id进行验证
如果用户没有授权,会尝试使用应用池的账户进行验证
如果应用池账号为NetworkService或LocalSystem,将使用计算机账号进行验证
采取这样的方式IIS会提出警告,因为IIS只有在运行之后,才知道能否访问NAS
C、通过代码实现

    //NAS访问封装类
    public class NetworkDrive
    {
        public enum ResourceScope
        {
            RESOURCE_CONNECTED = 1,
            RESOURCE_GLOBALNET,
            RESOURCE_REMEMBERED,
            RESOURCE_RECENT,
            RESOURCE_CONTEXT
        }

        public enum ResourceType
        {
            RESOURCETYPE_ANY,
            RESOURCETYPE_DISK,
            RESOURCETYPE_PRINT,
            RESOURCETYPE_RESERVED
        }

        public enum ResourceUsage
        {
            RESOURCEUSAGE_CONNECTABLE = 0x00000001,
            RESOURCEUSAGE_CONTAINER = 0x00000002,
            RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
            RESOURCEUSAGE_SIBLING = 0x00000008,
            RESOURCEUSAGE_ATTACHED = 0x00000010,
            RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
        }

        public enum ResourceDisplayType
        {
            RESOURCEDISPLAYTYPE_GENERIC,
            RESOURCEDISPLAYTYPE_DOMAIN,
            RESOURCEDISPLAYTYPE_SERVER,
            RESOURCEDISPLAYTYPE_SHARE,
            RESOURCEDISPLAYTYPE_FILE,
            RESOURCEDISPLAYTYPE_GROUP,
            RESOURCEDISPLAYTYPE_NETWORK,
            RESOURCEDISPLAYTYPE_ROOT,
            RESOURCEDISPLAYTYPE_SHAREADMIN,
            RESOURCEDISPLAYTYPE_DIRECTORY,
            RESOURCEDISPLAYTYPE_TREE,
            RESOURCEDISPLAYTYPE_NDSCONTAINER
        }

        [StructLayout(LayoutKind.Sequential)]
        private class NETRESOURCE
        {
            public ResourceScope dwScope = 0;
            public ResourceType dwType = 0;
            public ResourceDisplayType dwDisplayType = 0;
            public ResourceUsage dwUsage = 0;
            public string lpLocalName = null;
            public string lpRemoteName = null;
            public string lpComment = null;
            public string lpProvider = null;
        }

        [DllImport("mpr.dll")]
        private static extern int WNetAddConnection2(NETRESOURCE lpNetResource, string lpPassword, string lpUsername, int dwFlags);

        public static int MapNetworkDrive(string remotePath, string localDrive, string userName, string passWord)
        {
            NETRESOURCE myNetResource = new NETRESOURCE();
            myNetResource.lpLocalName = localDrive;
            myNetResource.lpRemoteName = remotePath;
            myNetResource.lpProvider = null;
            int result = WNetAddConnection2(myNetResource, passWord, userName, 0);
            return result;
        }
    }

    //调用示例
    NetworkDrive.MapNetworkDrive("NAS paht with ip", "Local Driver Letter", "user", "password");

参考资料:
What is “pass-through authentication” in IIS 7?

不支持一个STA线程上针对多个句柄的WaitAll

最近项目中用了COM控件,同时也用了多线程,在等待线程退出的地方,使用了

AutoResetEvent[] tEevents;
//...
WaitHandle.WaitAll(tEevents);

程序运行到这里,就会报错:

不支持一个STA线程上针对多个句柄的WaitAll

当时事情很急,将其调整为

AutoResetEvent[] tEevents;
//...
foreach(var tEvent in tEevents)
{
    tEvent.WaitOne();
}

就将这个问题绕了过去。

后来,在Stackoverflow上面,查了STA和MTA的区别。COM线程模型称为Apartment模型,分为STA和MTA两种。
STA(Single Thread Apartment)
STA是非线程安全的,常用于UI界面,其他线程要访问STA模式的COM组件,需要通过STA线程进行访问(其实就变成了单线程调用)
MTA(Multi Thread Apartment)
MTA是线程安全的,COM的程序员自己处理了并发的问题,其他线程可以直接访问COM组件

参考资料:
Could you explain STA and MTA?

CS进程单例模式

1、SingletonExeController.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace SingletonExeTest
{
    //通过检测Mutex,确认只会启用一个进程
    //第一个进程,会启用Tcp监听,接收参数
    //后续进程,通过TcpChannel,将参数传递给第一个进程
    //使用前,必须先调用InitSingleton,使用后必须调用UninitSingleton
    class SingletonExeController : MarshalByRefObject
    {
        private static Mutex m_Mutex = null;
        private static TcpChannel m_TCPChannel = null;
        
        //初始化
        public static void InitSingleton()
        {
            string assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName(false).CodeBase;
            string uniqueIdentifier = assemblyName.Replace("\\", "_");
            m_Mutex = new Mutex(false, uniqueIdentifier);
        }

        //回收资源
        public static void UninitSingleton()
        {
            if (m_Mutex != null)
            {
                m_Mutex.Close();
            }
            m_Mutex = null;

            if (m_TCPChannel != null)
            {
                m_TCPChannel.StopListening(null);
            }
            m_TCPChannel = null;
        }

        //判断是否为第一个进程
        public static bool FirstProcToRun(int tcpPort,String serviceName)
        {
            if (m_Mutex.WaitOne(1, true))
            {
                CreateTCPChannel(tcpPort,serviceName);
                return true;
            }
            else
            {
                m_Mutex.Close();
                m_Mutex = null;
                return false;
            }
        }

        //创建TCP监听
        private static void CreateTCPChannel(int tcpPort,String serviceName)
        {
            try
            {
                m_TCPChannel = new TcpChannel(tcpPort);
                ChannelServices.RegisterChannel(m_TCPChannel, false);
                RemotingConfiguration.RegisterWellKnownServiceType(typeof(SingletonExeController), serviceName, WellKnownObjectMode.SingleCall);
            }
            catch(Exception ex)
            {
                //Fix me...
                //port in use
                MessageBox.Show(ex.Message);
            }
        }

        //后续进程,向第一个进程发送自己的参数
        public static void Send(int port,String serviceName, string[] s)
        {
            try
            {
                SingletonExeController ctrl;
                TcpChannel channel = new TcpChannel();
                ChannelServices.RegisterChannel(channel, false);
                try
                {
                    String address = "tcp://localhost:" + port + "/" + serviceName;
                    ctrl = (SingletonExeController)Activator.GetObject(typeof(SingletonExeController), address);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception: " + e.Message);
                    throw;
                }
                ctrl.Receive(s);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        //第一个进程,接收传入参数
        public void Receive(string[] s)
        {
            //Do something here
            //...

            //Test Code
            MessageBox.Show(s[0]);
        }

        //查找第一个进程
        public static Process findMainProcess()
        {
            Process currentProcess = Process.GetCurrentProcess();
            string processName = currentProcess.ProcessName;

            Process mainProcess = null;
            Process[] allProcesses = Process.GetProcessesByName(processName);
            if (allProcesses != null)
            {
                foreach (Process p in allProcesses)
                {
                    if (p.Id != currentProcess.Id)
                    {
                        if (p.MainWindowHandle != null)
                        {
                            mainProcess = p;
                            break;
                        }
                    }
                }
            }

            return mainProcess;
        }

        //查找第一个进程的主窗口
        public static IntPtr findMainWnd()
        {
            IntPtr mainWnd = IntPtr.Zero;
            Process p = findMainProcess();
            if(p!=null && p.MainWindowHandle != null)
            {
                mainWnd=p.MainWindowHandle;
            }

            return mainWnd;
        }
    }
}

2、Program.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;

namespace SingletonExeTest
{
    static class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            //Debugger.Launch();
            //Debugger.Break();

            int tcpPort = 9999;
            String serviceName = "MyTestService";

            SingletonExeController.InitSingleton();

            if (SingletonExeController.FirstProcToRun(tcpPort, serviceName))
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);

                //Do something here
                //...

                //Test Code
                Application.Run(new MainForm());
            }
            else
            {
                IntPtr mainWnd = SingletonExeController.findMainWnd();
                Win32Utils.ShowWindow(mainWnd, Win32Utils.SW_RESTORE);
                //Real Code
                //SingletonExeController.Send(tcpPort, serviceName, args);

                //Test Code
                String[] msgs = {"hi","hello","good"};
                SingletonExeController.Send(tcpPort, serviceName, msgs);
            }

            SingletonExeController.UninitSingleton();
        }
    }
}

CS获取可执行文件所在文件夹

        //Form程序
        private static String getExeFileFolder()
        {
            String strExeFolder = System.Reflection.Assembly.GetExecutingAssembly().Location;
            int nPos = strExeFolder.LastIndexOf("\\");
            if (nPos >= 0)
            {
                strExeFolder = strExeFolder.Substring(0, nPos + 1);
            }
            else
            {
                strExeFolder = strExeFolder + "\\";
            }

            return strExeFolder;
        }

        //IIS程序
        String rootPath = Request.PhysicalApplicationPath;
        String strExeFolder = getExeFileFolder(rootPath);
        private static String getExeFileFolder(String strExeFolder)
        {
            int nPos = strExeFolder.LastIndexOf("\\");
            if (nPos >= 0)
            {
                strExeFolder = strExeFolder.Substring(0, nPos + 1);
            }
            else
            {
                strExeFolder = strExeFolder + "\\";
            }

            return strExeFolder;
        }

多线程处理

        int nMaxThreadNum = 5;
        List<List<JobParam>> jobQueue = new List<List<JobParam>>();
        //...
        for (int i = 0; i < nMaxThreadNum;i++)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncDoRetrieve), jobQueue&#91;i&#93;);
        }

        static void AsyncDoRetrieve(object state)
        {
            if (!(state == null && state is List<JobParam>)) return;
            List<JobParam> jobParams= state as List<JobParam> ;
            foreach(JobParam param in jobParams)
            {
                param.delegateDoSomeThing.BeginInvoke(param,null,null);
            }
        }

DBHelperSQLLite

1.IDBHelper.cs

    interface IDBHelper
    {
        void initDB();
        void closeDB();
        void ExecSQL(String execSql);
        DataTable ExecQuery(String querySql);
    }

2.ZDBHelperSQLLite.cs

    class ZDBHelperSQLLite : IDBHelper
    {
        String dataSource = "";
        SQLiteConnection connection = null;
        public void initDB(String dbPath)
        {
            dataSource = "Data Source =" + dbPath();
            connection = new SQLiteConnection(dbFile);
            connection.Open();
            initTables();
            CreateFileInfoTable();
        }
        
        public void closeDB()
        {
            connection.Close();
        }

        private void initTables()
        {
            String sqlCreate01 = "CREATE TABLE IF NOT EXISTS visitlog(user_id varchar(64), visit_path varchar(256), visit_time integer);";
            SQLiteCommand cmdCreateTable = new SQLiteCommand(connection);
            cmdCreateTable.CommandText = sqlCreate01;
            cmdCreateTable.ExecuteNonQuery();
        }
  
        public void ExecSQL(String execSql)
        {
            SQLiteTransaction transaction = connection.BeginTransaction();
            SQLiteCommand cmdExec = new SQLiteCommand(execSql, connection, transaction );
            cmdExec.ExecuteNonQuery();
            transaction.Commit();
        }

        public DataTable ExecQuery(String querySql)
        {
            SQLiteCommand cmdQuery = new SQLiteCommand(querySql, connection);
            SQLiteDataReader reader = cmdQuery.ExecuteReader();

            DataTable datatable = new DataTable();
            DataTable schemaTable = reader.GetSchemaTable();
            foreach (DataRow myRow in schemaTable.Rows)
            {
                DataColumn myDataColumn = new DataColumn();
                myDataColumn.DataType = myRow.GetType();
                myDataColumn.ColumnName = myRow[0].ToString();
                datatable.Columns.Add(myDataColumn);
            }
            schemaTable = null;

            while (reader.Read())
            {
                DataRow myDataRow = datatable.NewRow();
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    myDataRow[i] = reader[i].ToString();
                }
                datatable.Rows.Add(myDataRow);
                myDataRow = null;
            }
            reader.Close();

            return datatable;
        }
    }

C#写WindowsService

1、新建一个Windows Service项目,重命名服务类名

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;

namespace ServiceTestCS
{
    public partial class ServiceTestCS : ServiceBase
    {
        //安装卸载时,最好用绝对路径哦
        //Installutil NASTestCS
        //Installutil -u NASTestCS

        private const int waitInterval = 200;
        private AutoResetEvent stopEvent = new AutoResetEvent(false);
        private AutoResetEvent pauseContinueEvent = new AutoResetEvent(false);
        private Thread workingThread = null;

        public NASTestCS()
        {
            InitializeComponent();
            this.CanPauseAndContinue = true;
            this.CanShutdown = true;
        }

        protected override void OnStart(string[] args)
        {
            #if DEBUG
            /*
            //等待调试器,但调试Windows服务时,提示没有加载符号
            while (!Debugger.IsAttached)
            {
                Thread.Sleep(waitInterval);
            }
            */
            Debugger.Launch();
            #endif

            workingThread = new Thread(new ThreadStart(MaiLoop));
            workingThread.Start();
        }

        protected override void OnStop()
        {
            stopEvent.Set();
        }

        protected override void OnShutdown()
        {
            stopEvent.Set();
        }

        protected override void OnPause()
        {
            pauseContinueEvent.Set();
        }

        protected override void OnContinue()
        {
            pauseContinueEvent.Set();
        }

        protected void MaiLoop()
        {
            Boolean bEnd = false;
            while (!bEnd)
            {
                //处理你的正常事务
                //但要保证下面的代码,可以定期被调用到
                //......

                //截获停止事件
                if(stopEvent.WaitOne(waitInterval))
                {
                    bEnd = true;
                    break;
                }

                //截获暂停事件
                if (pauseContinueEvent.WaitOne(waitInterval))
                {
                    //等待继续时间
                    while (!pauseContinueEvent.WaitOne(waitInterval))
                    {
                        //少占用一些资源
                        Thread.Sleep(waitInterval);

                        //暂停时也可以退出
                        if (stopEvent.WaitOne(waitInterval))
                        {
                            bEnd = true;
                            break;
                        }
                    }
                }
            }
        }
    }
}

2、服务设计界面右击,添加Service Installer
配置Service Installer,配置服务类名称,显示名称,启动类型,登录用户,服务依赖等
但如果要可以与桌面交互的话,需要添加代码实现。
双击serviceInstaller,添加下面方法:

        using System.Management;

        private void serviceInstaller_AfterInstall(object sender, InstallEventArgs e)
        {
            //要用LocalSystem用户登录才有效哦
            ManagementObject wmiService = new ManagementObject(string.Format("Win32_Service.Name='{0}'", this.serviceInstaller.ServiceName));
            ManagementBaseObject changeMethod = wmiService.GetMethodParameters("Change");
            changeMethod["DesktopInteract"] = true;
            ManagementBaseObject OutParam = wmiService.InvokeMethod("Change", changeMethod, null);
        }

3、安装与反安装,用命令行实现

rem 安装服务
%PATH_TO_INSTALLUTIL%\installutil.exe %PATH_TO_SERVICE%\NASTestCS.exe
rem 卸载服务
%PATH_TO_INSTALLUTIL%\installutil.exe -u %PATH_TO_SERVICE%\NASTestCS.exe

使用命名管道实现进程间通信(下)

1、服务端C#

        private const String MY_PIPE_NAME = "__MY__PIPE__TEST__";
        private const int BUFFER_SIZE = 1024;
        NamedPipeServerStream pipe;
        StreamWriter writer;
        String msg = "Message is comming";

        private void PipeCreate()
        {
            if (pipe != null && pipe.IsConnected)
            {
                pipe.Close();
            }

            pipe = new NamedPipeServerStream(MY_PIPE_NAME, PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.None);
            

            if (pipe == null)
            {
                textBox1.Text = textBox1.Text + "Pipe is null";
                return;
            }

            if (pipe.IsConnected)
            {
                textBox1.Text = textBox1.Text + "Pipe is already connected";
                return;
            }

            pipe.WaitForConnection();

            textBox1.Text = textBox1.Text + "Pipe is ready to connect\r\n";
        }

        private void PipeWrite()
        {
            if (pipe!=null && pipe.IsConnected)
            {
                if(writer==null)
                {
                    //关闭BOM(Byte Order Mark 0xfeff)
                    UnicodeEncoding unicodeWithoutBom = new System.Text.UnicodeEncoding(false, false);
                    writer = new StreamWriter(pipe, unicodeWithoutBom);
                    //写完后直接flush,会阻塞
                    writer.AutoFlush = true;
                }
                writer.Write(msg);

                textBox1.Text = textBox1.Text + "Pipe message sent \r\n";
            }
            else
            {
                textBox1.Text = textBox1.Text + "Pipe is not connected \r\n";
            }
        }

        private void PipeClose()
        {
            if (pipe!=null && pipe.IsConnected)
            {
                writer.Close();
                pipe.Close();

                writer = null;
                pipe = null;
            }
        }

2、客户端C#

        private const String MY_PIPE_NAME =  "__MY__PIPE__TEST__";
        private const int BUFFER_SIZE = 1024;
        NamedPipeClientStream pipe;
        StreamReader reader;

        private void PipeConnect()
        {
            if (pipe != null && pipe.IsConnected)
            {
                pipe.Close();
            }
            pipe = new NamedPipeClientStream(".", MY_PIPE_NAME, PipeDirection.InOut);

            if (pipe != null)
            {
                if (!pipe.IsConnected)
                {
                    pipe.Connect();
                    textBox1.Text = textBox1.Text + "\r\npipe is connected";
                }
                else
                {
                    textBox1.Text = textBox1.Text + "\r\npipe is already connected";
                }
            }
            else
            {
                textBox1.Text = textBox1.Text + "\r\npipe is null";
            }

            
        }

        private void PipeRead()
        {
            if (pipe.IsConnected)
            {
                if (reader == null)
                {
                    reader = new StreamReader(pipe, Encoding.Unicode);
                }

                char[] buffer = new char[BUFFER_SIZE];
                int byteRead = reader.Read(buffer, 0, BUFFER_SIZE);
                String msgTxt = new String(buffer, 0, byteRead);
                textBox1.Text = textBox1.Text + "\r\nPipe msg received: " + msgTxt;
            }
            else
            {
                textBox1.Text = textBox1.Text + "\r\nPipe is not connected";
            }
        }

        private void PipeClose()
        {
            if (reader != null)
            {
                reader.Close();
                reader = null;
            }
            if (pipe != null && pipe.IsConnected)
            {
                pipe.Close();
            }

            textBox1.Text = textBox1.Text + "\r\npipe is closed";
        }

匿名管道重定向命令行输出

首先是MFC,注意事项:
1、管道读写是FIFO
2、读写指针,要记得关闭
3、编译时用了UNICODE,但CMD读取回来时ANSI,所以要转换一下字符集

	SECURITY_ATTRIBUTES sa;
	ZeroMemory(&sa, sizeof(sa));
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;

	HANDLE hRead, hWrite;
	if (!CreatePipe(&hRead, &hWrite, &sa, 0)) {
		MessageBox(L"Error On CreatePipe()");
		return;
	}

	STARTUPINFO si;
	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(STARTUPINFO);
	GetStartupInfo(&si);
	si.hStdError = hWrite;
	si.hStdOutput = hWrite;
	si.wShowWindow = SW_HIDE;
	si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
	PROCESS_INFORMATION pi;
	ZeroMemory(&pi, sizeof(pi));
	if (!::CreateProcess(L"C:\\Windows\\System32\\cmd.exe", L"/c dir /b D:\\Downloads"
		, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi)) {
		showMeErrorInfo();	
		return;
	}
	CloseHandle(hWrite);

	char buffer[4096] = { 0 };
	DWORD bytesRead;
	while (true) {
		if (ReadFile(hRead, buffer, 4095, &bytesRead, NULL) == NULL)
		{
			break;
		}
		
		UINT CodePage = 0;
		DWORD dwNum;
		dwNum = MultiByteToWideChar(CodePage, 0, buffer, -1, NULL, 0);
		if (dwNum)
		{
			wchar_t *pwText;
			pwText = new TCHAR[dwNum];
			if (pwText)
			{
				MultiByteToWideChar(CodePage, 0, buffer, -1, pwText, dwNum);
			}
			//m_Edit.SetWindowText(pwText);
			delete[]pwText;
			pwText = NULL;
		}
		UpdateData(false);
		Sleep(200);
	}
	CloseHandle(hRead);

C#的话,就简单多了:

            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = "C:\\Windows\\System32\\cmd.exe";
            startInfo.Arguments = "/c dir /b D:\\Downloads";
            startInfo.RedirectStandardOutput = true;
            //startInfo.RedirectStandardError = true;
            startInfo.UseShellExecute = false;
            startInfo.CreateNoWindow = false;
           
            Process p=Process.Start(startInfo);

            String cmdOut = "";
            while(!p.HasExited)
            { 
                cmdOut = p.StandardOutput.ReadLine();
                textBox1.Text += cmdOut + "\r\n";
                p.WaitForExit(10);
            }
            cmdOut = p.StandardOutput.ReadToEnd() + "\r\n";
            textBox1.Text += cmdOut ;