C#探祕系列(四)

WindowsForm窗口與exe文件之間的通信

Windows窗體應用程序有時需要調用已編譯好的exe文件,但是如何將窗體程序中的數據導入exe文件,並將結果返回到窗體界面上,從而實現窗體與exe文件之間的通信呢?例如,我在窗體中分別添加一個textBox、button和listBox,在textBox中輸入某些參數,點擊button,將數據傳至exe文件中進行處理,並將結果返回至窗體中的listBox以呈現給用戶。下面,我先以一個簡單的demo爲例,來詳述通信過程並剖析其中要點。爲了簡便,我略去了exe文件對數據的處理過程,這個過程要視具體程序而定,而我只是將WindowsForm中輸入的數據經exe文件又反饋給了listBox。

一、Demo示例

1.首先,新建一個控制檯應用程序(OutPutString):

#region directive
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
#endregion

namespace OutPutString
{
    class Program
    {
        static void Main(string[] args)
        {
            //Create a String to hold the data of the console
            String strToInput = Console.ReadLine();
            //Print the String to the console
            Console.WriteLine(strToInput);
        }
    }
}

代碼異常簡單,其實現的功能是從控制檯上獲取手動輸入的字符串,並將其顯示於控制檯上。按F5將其編譯,並生成exe文件。2.第二步,要新建一個Windows窗體程序以實現用戶交互。利用工具箱分別將TextBox、Button及ListBox拖至虛擬窗體中,並將其重新命名,單擊窗體中的Button生成Click函數,並定義其功能,如下:
1)Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace OutPutStringForm
{
    static class Program
    {
        /// <summary>
        /// 應用程序的主入口點。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

2)Form1.Designer.cs

namespace OutPutStringForm
{
    partial class Form1
    {
        /// <summary>
        /// 必需的設計器變量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 清理所有正在使用的資源。
        /// </summary>
        /// <param name="disposing">如果應釋放託管資源,爲 true;否則爲 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows 窗體設計器生成的代碼

        /// <summary>
        /// 設計器支持所需的方法 - 不要
        /// 使用代碼編輯器修改此方法的內容。
        /// </summary>
        private void InitializeComponent()
        {
            this.button = new System.Windows.Forms.Button();
            this.listBox = new System.Windows.Forms.ListBox();
            this.textBox = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            // 
            // button
            // 
            this.button.BackColor = System.Drawing.SystemColors.Highlight;
            this.button.Location = new System.Drawing.Point(176, 23);
            this.button.Name = "button";
            this.button.Size = new System.Drawing.Size(75, 42);
            this.button.TabIndex = 0;
            this.button.Text = "ClickMe";
            this.button.UseVisualStyleBackColor = false;
            this.button.Click += new System.EventHandler(this.button_Click);
            // 
            // listBox
            // 
            this.listBox.FormattingEnabled = true;
            this.listBox.ItemHeight = 15;
            this.listBox.Location = new System.Drawing.Point(31, 86);
            this.listBox.Name = "listBox";
            this.listBox.Size = new System.Drawing.Size(220, 94);
            this.listBox.TabIndex = 1;
            // 
            // textBox
            // 
            this.textBox.AllowDrop = true;
            this.textBox.Location = new System.Drawing.Point(31, 23);
            this.textBox.Name = "textBox";
            this.textBox.Size = new System.Drawing.Size(125, 25);
            this.textBox.TabIndex = 2;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(282, 253);
            this.Controls.Add(this.textBox);
            this.Controls.Add(this.listBox);
            this.Controls.Add(this.button);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button button;
        private System.Windows.Forms.ListBox listBox;
        private System.Windows.Forms.TextBox textBox;
    }
}

窗體圖例:
WindowsFormDemo
3)Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace OutPutStringForm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button_Click(object sender, EventArgs e)
        {
            //String to get the text of the textBox
            String strToInput = textBox.Text;

            //Process to start the exe file
            Process myProcess = new Process();
            ProcessStartInfo myProcessStartInfo = new ProcessStartInfo("E:\\CodePackets\\OutPutString\\OutPutString\\bin\\Debug\\OutPutString.exe");
            myProcessStartInfo.UseShellExecute = false;
            myProcessStartInfo.RedirectStandardInput = true;
            myProcessStartInfo.RedirectStandardOutput = true;
            myProcess.StartInfo = myProcessStartInfo;
            myProcess.Start();

            //Stream to put data into the exe file
            StreamWriter myStreamWriter = myProcess.StandardInput;
            myStreamWriter.WriteLine(strToInput);

            //Stream to read the output data of the exe file
            StreamReader myStreamReader = myProcess.StandardOutput;
            // Read the standard output of the spawned process.
            string myString = myStreamReader.ReadLine();
            listBox.Items.Add(myString);
            myProcess.Close();
            listBox.Show();
        }
    }
}

程序結果如下:
WindowsForm2 for OutPut

二、利用Process.StartInfo 屬性調用exe文件

在Demo中,我們開啓了一個進程,並利用Process的StartInfo屬性來獲取或設置要傳遞給 Process 的 Start 方法的屬性。ProcessStartInfo 表示啓動該進程時要使用的數據。 這些參數包括用於啓動該進程的可執行文件或文檔的名稱。

調用 Start 時,StartInfo 用於指定要啓動的進程。 唯一必須設置的 StartInfo 成員是 FileName 屬性。 通過指定 FileName 屬性來啓動進程,這樣做類似於在 Windows“開始”菜單的“運行”對話框中鍵入信息。 因此,FileName 屬性不需要表示可執行文件。 它可以是其擴展名已經與系統上安裝的應用程序關聯的任何文件類型。 例如,如果已經將文本文件與某個編輯器(如“記事本”)關聯,則 FileName 可以具有 .txt 擴展名;如果已經將 .doc 文件與某個字處理工具(如 Microsoft Word)關聯,則它可以具有 .doc 擴展名。 同樣,“運行”對話框可以以相同的方式接受帶有或不帶 .exe 擴展名的可執行文件名,.exe 擴展名在 FileName 成員中是可選的。 例如,可將 FileName 屬性設置爲“Notepad.exe”或“Notepad”。

如果文件名涉及不可執行文件(如 .doc 文件),則可以包括一個謂詞指定要對該文件執行什麼操作。 例如,對於以 .doc 擴展名結尾的文件,可以將 Verb 設置爲“Print”。 如果您手動爲 Verb 屬性輸入一個值,則在 FileName 屬性中指定的文件名不需要具有擴展名。 但是,如果您使用 Verbs 屬性來確定哪些謂詞可用,則必須包括文件擴展名。

直到在進程上調用 Start 方法時才能更改 StartInfo 屬性中指定的參數。 啓動該進程之後,更改 StartInfo 值不會影響也不會重新啓動關聯的進程。 如果在設置了 ProcessStartInfo.UserName 和 ProcessStartInfo.Password 屬性後調用 Start(ProcessStartInfo) 方法,則將調用非託管 CreateProcessWithLogonW 函數,這樣,即使 CreateNoWindow 屬性值爲 true 或者 WindowStyle 屬性值爲 Hidden,也將在新窗口中啓動該進程。

如果未使用 Start 方法啓動進程,則 StartInfo 屬性不會反映啓動該進程時使用的參數。 例如,如果使用 GetProcesses 獲取計算機上運行的進程的數組,則每個 Process 的 StartInfo 屬性將不包含啓動進程時使用的原始文件名稱或參數。

啓動進程後,文件名是填充(只讀)MainModule 屬性的文件。 如果要在進程啓動後檢索與進程關聯的可執行文件,請使用 MainModule 屬性。 如果要設置尚未爲其啓動關聯進程的 Process 實例的可執行文件,請使用 StartInfo 屬性的 FileName 成員。 因爲 StartInfo 屬性的成員是要傳遞給進程的 Start 方法的參數,所以,啓動關聯進程後更改 FileName 屬性不會重置 MainModule 屬性。 這些屬性僅用於初始化關聯的進程。
下面的例子打開了名爲”HelloWorld.exe”的文件:

using System;
using System.Diagnostics;
using System.ComponentModel;

namespace MyProcessSample
{
    class MyProcess
    {
        public static void Main()
        {
            Process myProcess = new Process();

            try
            {
                myProcess.StartInfo.UseShellExecute = false;
                // You can start any process, HelloWorld is a do-nothing example.
                myProcess.StartInfo.FileName = "C:\\HelloWorld.exe";
                myProcess.StartInfo.CreateNoWindow = true;
                myProcess.Start();
                // This code assumes the process you are starting will terminate itself. 
                // Given that is is started without a window so you cannot terminate it 
                // on the desktop, it must terminate itself or you can do it programmatically
                // from this application using the Kill method.
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}

這裏要注意,指定的文件名中間用雙右斜線劃分開,路徑後一定要記得加exe文件的全稱,否則可能會拋出System.ComponentModel.Win32Exception的異常。

三、StreamWriter與StreamReader

C#與Java、C++的讀寫操作不同,不再區分字符流、字節流,而統一採用Stream方式,然後針對Stream提供Reader與Writer的相關操作。
StreamWriter向流中輸入數據,StreamReader用來獲取流中的數據。

下面的示例演示如何使用 StreamWriter 對象寫入一個列出 C 驅動器上目錄的文件,然後使用 StreamReader 對象讀取和顯示每個目錄的名稱。 一種好的做法是使用 using 語句中的這些對象以便正確釋放非託管資源。 using 語句在使用它的代碼完成後,將自動調用對象上的 Dispose。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace StreamReadWrite
{
    class Program
    {
        static void Main(string[] args)
        {
            // Get the directories currently on the C drive.
            DirectoryInfo[] cDirs = new DirectoryInfo(@"c:\").GetDirectories();

            // Write each directory name to a file.
            using (StreamWriter sw = new StreamWriter("CDriveDirs.txt"))
            {
                foreach (DirectoryInfo dir in cDirs)
                {
                    sw.WriteLine(dir.Name);

                }
            }

            // Read and show each line from the file.
            string line = "";
            using (StreamReader sr = new StreamReader("CDriveDirs.txt"))
            {
                while ((line = sr.ReadLine()) != null)
                {
                    Console.WriteLine(line);
                }
            }
        }
    }
}
發佈了29 篇原創文章 · 獲贊 12 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章