事の発端
それは TwitterのAPIとPythonとTweepyを使って、使いづらいブックマークを自分用に使いやすくしようとしていた時であった。
TwitterのAPIを使う部分はPython
で、収集したデータを表示するのをC#フォーム
で作っていた。
C#
からPythonスクリプト
を起動できないかと思って調べていた。
検索して出てきたコードは以下の通り。
var process = new Process()
{
StartInfo = new ProcessStartInfo("python.exe")
{
UseShellExecute = false,
RedirectStandardOutput = true,
Arguments = new string[]
{
"Test.py" //,
//... コマンドライン引数
}
}
};
サイトによって"Python.exe"
の部分が絶対パスであったり、いくつかの細かい部分に違いはあれど、基本的に上記のプログラムが書かれていた。
実際この方法で起動してみたものの、一瞬だけPython.exeのプロンプト(?)が表示され、Pythonスクリプトは作動しなかった。
Pythonのコード自体に文法ミスやエラーとなり得るものは無かった。
その後、StartInfo
のWorkingDirectory
を指定したりpython.exe
ではなくpy.exe
(C:\\Windows\\py.exe
)を指定して見たりと、試行錯誤をしたものの全て撃沈。
同じ症状がないかと検索してみたものの、ヒットするものは無かった。
うまく"動作"させるには?
実際、1年かな2年かな、いつだか忘れたが以前にも同じような症状に苦しめられた時があった。
たくさんあったゲームをひとまとめにしようと、ランチャーを作ったことがあった。
その時、某(´・ω・`)鬼畜ゲームをProcess.Start
で起動した時に、なぜかリソースが読み込まれなかった。
その時に編み出した方法は、コマンドプロンプトを経由させる方法だった。
結局今回もそれに頼ることにした...。
以前の私はStart.bat
を手動で用意して、それを起動させるように指定したのだが、今回はバッチファイルを生成して、起動、削除まで自動で行うことにした。
この方法はおそらく良くないだろう... 何かほかの解決策があるなら教えてください。
それでは、アプリケーションの階層が次のようだとする。
📁 TwitterAPI_App
├─ 📄 GetTweet.py (Process.Startで起動したいPythonスクリプト)
├─ 📄 TwitterAPI_Test.exe (アプリケーション)
└─ 📄 TwitterAPI_Test.exe.config
この場合は次のようなコードになる。
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
namespace TwitterAPI_App
{
public partial class Form1 : Form
{
private void LaunchPython(object sender, EventArgs e)
{
// バッチファイルの作成
string curDict = Directory.GetCurrentDirectory();
string batfilePath = Path.Combine(curDict, "Start.bat");
string[] batfileData = new string[2];
batfileData[0] = "@echo off"; // コマンドプロンプトに結果を表示しないだけなので、任意
batfileData[1] = "cmd /c start GetTweet.py"; // "cmd /c"は任意
File.WriteAllLines(batfilePath, batfileData);
// プロセスの開始
Process cmd = new Process();
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.FileName = batfilePath;
//cmd.StartInfo.CreateNoWindow = true; // 任意
cmd.Start();
// 終わるのを待ってからファイルを削除
cmd.WaitForExit();
// cmd.Close() // "cmd /c"が無いならおそらく必要
File.Delete(batfilePath);
}
}
}
もう1パターンある。 それはアプリケーションの階層が次のような時だ。
📁 TwitterAPI_App
├─ 📁 TwitterAPI
│ └─ 📄 GetTweet.py (Process.Startで起動したいPythonスクリプト)
├─ 📄 TwitterAPI_Test.exe (アプリケーション)
└─ 📄 TwitterAPI_Test.exe.config
この場合はbatファイルの内容が少しだけ変わる。コードは変わる部分のみを書き記す。
namespace TwitterAPI_App
{
public partial class Form1 : Form
{
private void LaunchPython(object sender, EventArgs e)
{
// ...
string batfilePath = Path.Combine(curDict, "TwitterAPI", "Start.bat");
string[] batfileData = new string[3]; // フォルダの数だけ増える
// string と File.WriteAllText 使う方法もあるけど ここでは省略
batfileData[0] = "@echo off";
batfileData[1] = "cd \"TwitterAPI\""; // 必ずしも\"\"で囲む必要は無いが、フォルダ名に空白があるなら必要
batfileData[2] = "cmd /c start GetTweet.py";
// ...
}
}
}
Start.bat
をどこに生成しようが、 "cd \"TwitterAPI\""
のくだりは必要となる。
(StartInfo.WorkingDirectory
を指定すれば必要がないかもしれないけど)
以上です。
ちなみに`cmd.StartInfo.CreateNoWindow = true;`を指定しても、Pythonのウインドウは絶対開くよ。 ('START'になんらかの引数渡せば何とかなるとは思うけど)