2026/01/11(日) [n年前の日記]
#2 [csharp] C#から外部プログラムを呼び出すところでハマった
C#から外部プログラムを呼び出す処理でちょっとハマった。ググった感じでは、Process.Start() を使えば呼び出せるということは分かったのだけど…。
_C#でProcessを使って別アプリを実行(起動)する方法 #C# - Qiita
_外部アプリケーションを起動する、ファイルを関連付けられたソフトで開く - .NET Tips (VB.NET,C#...)
_プロセス | C# プログラミング解説
_C#で別EXEをパラメータ渡しで実行する #.NET - Qiita
単に、1つの .exe を呼び出すだけなら動作してくれた。
しかし、"python.exe C:/hoge/fuga/piyo.pyw" のような形で呼び出そうとすると「ソレ、実行できないよ」とエラーが出てしまう。
おそらくだけど、プログラムと引数をちゃんと分けて指定してやらないと実行できないっぽい。前述の事例の場合、プログラム名は "python.exe"、引数は "C:/hoge/fuga/piyo.pyw" に分解しないと…。
Google Gemini に尋ねてみたら、以下のような処理を提示された。Windowsには、CommandLineToArgvW() というAPI?があるので、それを利用すれば分解できるっぽい。
Form1.cs
_CommandLineToArgvW - 車輪のx発明 - B.G's Blog
_コマンドライン文字列の解析 - Qiita
たしかに動作してくれた。
動作してくれたけど…。考えてみたら、プログラム名と引数を別々に指定してもらうほうが確実なのかなと思えてきた。結局、設定ダイアログ上で、テキストボックスを2つ用意して対処することにしてしまった…。
_C#でProcessを使って別アプリを実行(起動)する方法 #C# - Qiita
_外部アプリケーションを起動する、ファイルを関連付けられたソフトで開く - .NET Tips (VB.NET,C#...)
_プロセス | C# プログラミング解説
_C#で別EXEをパラメータ渡しで実行する #.NET - Qiita
単に、1つの .exe を呼び出すだけなら動作してくれた。
しかし、"python.exe C:/hoge/fuga/piyo.pyw" のような形で呼び出そうとすると「ソレ、実行できないよ」とエラーが出てしまう。
おそらくだけど、プログラムと引数をちゃんと分けて指定してやらないと実行できないっぽい。前述の事例の場合、プログラム名は "python.exe"、引数は "C:/hoge/fuga/piyo.pyw" に分解しないと…。
Google Gemini に尋ねてみたら、以下のような処理を提示された。Windowsには、CommandLineToArgvW() というAPI?があるので、それを利用すれば分解できるっぽい。
Form1.cs
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace CsProcessStartSample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == DialogResult.OK)
{
textBox1.Text = ofd.FileName;
}
}
private void button1_Click(object sender, EventArgs e)
{
// textBox に入っている文字列で外部プログラムを実行する
string cmdline = textBox1.Text;
if (cmdline == "")
{
MessageBox.Show("Empty file path.");
return;
}
// プログラム名と引数に分ける
string filename;
string arguments;
(filename, arguments) = ParseCommandLine(cmdline);
if (filename == null || filename == "")
{
MessageBox.Show("Not found .exe", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
label2.Text = "0: [" + filename + "]";
label3.Text = "1: [" + arguments + "]";
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = filename,
Arguments = arguments,
UseShellExecute = true
};
// 外部プログラムを実行
Process.Start(psi);
}
public static (string fileName, string arguments) ParseCommandLine(string commandLine)
{
// Windows APIを使用して引数を配列に分解
string[] args = Win32CommandLineParser.SplitArgs(commandLine);
if (args.Length > 0)
{
string fileName = args[0];
// 2番目以降の要素をスペースで結合して引数文字列を作る
string arguments = string.Join(" ", args.Skip(1).Select(a => a.Contains(" ") ? $"\"{a}\"" : a));
return (fileName, arguments);
}
return ("", "");
}
public static class Win32CommandLineParser
{
[DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs);
public static string[] SplitArgs(string commandLine)
{
IntPtr ptr = CommandLineToArgvW(commandLine, out int numArgs);
if (ptr == IntPtr.Zero) return Array.Empty<string>();
try
{
string[] args = new string[numArgs];
for (int i = 0; i < numArgs; i++)
{
IntPtr p = Marshal.ReadIntPtr(ptr, i * IntPtr.Size);
args[i] = Marshal.PtrToStringUni(p);
}
return args;
}
finally { Marshal.FreeHGlobal(ptr); }
}
}
}
}
_CommandLineToArgvW - 車輪のx発明 - B.G's Blog
_コマンドライン文字列の解析 - Qiita
たしかに動作してくれた。
動作してくれたけど…。考えてみたら、プログラム名と引数を別々に指定してもらうほうが確実なのかなと思えてきた。結局、設定ダイアログ上で、テキストボックスを2つ用意して対処することにしてしまった…。
[ ツッコむ ]
以上です。