c4se記:さっちゃんですよ☆

.。oO(さっちゃんですよヾ(〃l _ l)ノ゙☆)

.。oO(此のblogは、主に音樂考察Programming に分類されますよ。ヾ(〃l _ l)ノ゙♬♪♡)

音樂は SoundCloud に公開中です。

考察は現在は主に Scrapbox で公表中です。

Programming は GitHub で開發中です。

C# からbatファイルを呼ぶにはSystem.Diagnostics.Processを使う

C# からbatファイルを呼ぶには、System.Diagnostics.Processを使う。
コマンドラインで動作するプログラムをRuby等で書いたはいいものの、いちいちcmdから呼ぶのは鬱陶しい。引数も与えなきゃいけないし、GUI作ってもいいじゃない、と思う。既存のこいつをそのまま使いたいし、楽に済ませたい。
先ずはそのコマンドラインプログラムを呼ぶbatファイルを書く。例えば、

cd C:\User\username
(
pik use 1.9.3
bundle exec ruby somescript.rb %1
)

の様にやる。
次にVisualStudioを立ち上げ、適当にXAMLGUIを作る。batを呼び出すには以下の様にする。

using System.Diagnostics;
using System.Windows;

namespace SomeApp
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void buttonAction_Click(object sender, RoutedEventArgs e)
        {
            labelStatus.Content = "Wait...";
            string param = textBoxParam.Text;
            int exitCode = CallBatch(param);
            if (exitCode == 0) labelStatus.Content = "Done!";
            else labelStatus.Content = "Failed.";
        }

        private int CallBatch(string param)
        {
            var startInfo = new ProcessStartInfo();
            startInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec");
            startInfo.CreateNoWindow = true;
            startInfo.UseShellExecute = false;
            startInfo.Arguments = string.Format(@"/c somebat.bat {0}", param);
            Process process = Process.Start(startInfo);
            process.WaitForExit();
            return process.ExitCode;
        }
    }
}

System.Diagnostics.Processでcmd.exeを立ち上げてやる。Processの引数にはSystem.Diagnostics.ProcessStartInfoを与えてやる。ProcessStartInfoFileNameにcmd.exeを与えてやり、Arguments/cオプションを付けてbatファイルのpathを入れる。
batの呼び出しを非同期にしてなくて悲しい。

追記 20120729

非同期にした。.NET Framework 4ならasync, awaitが使える筈だが、.NET Framework 3.5なのでThreadを書く。

using System;
using System.Diagnostics;
using System.Threading;
using System.Windows;

namespace SomeApp
{
    public partial class Window1 : Window
    {
        private string Param;

        public Window1()
        {
            InitializeComponent();
        }

        private void buttonAction_Click(object sender, RoutedEventArgs e)
        {
            labelStatus.Content = "Wait...";
            Param = textBoxParam.Text;
            var thread = new Thread(new ThreadStart(ThreadEntry));
            thread.IsBackground = true;
            thread.Start();
        }

        private void ThreadEntry()
        {
            int exitCode = CallBatch(Param);
            Dispatcher.BeginInvoke((Action)delegate()
            {
                if (exitCode == 0) labelStatus.Content = "Done!";
                else labelStatus.Content = "Failed.";
            });
        }

        /// <summary>
        /// This may take while.
        /// </summary>
        /// <param name="param"></param>
        /// <returns>Exit code.</returns>
        private int CallBatch(string param)
        {
            var startInfo = new ProcessStartInfo();
            startInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec");
            startInfo.CreateNoWindow = true;
            startInfo.UseShellExecute = false;
            startInfo.Arguments = string.Format(@"/c somebat.bat {0}", param);
            Process process = Process.Start(startInfo);
            process.WaitForExit();
            return process.ExitCode;
        }
    }
}

呼び出されたthreadからは、Window側のthisを見れない事に注意。compileは通るが、実行時errorに成る。依ってDispatcher.BeginInvoke(Delegate action, object[] param)を使い非同期に実行する。行って (Thread) 戻る (Invoke) ので、二重の非同期。