مقدمه

برنامه‌های ما در انزوا اجرا نمی‌شوند. اغلب اوقات، نیاز داریم تا با دیگر برنامه‌ها و ابزارهای موجود در سیستم‌عامل تعامل داشته باشیم. این تعامل می‌تواند شامل اجرای یک ابزار خط فرمان، باز کردن یک فایل با نرم‌افزار پیش‌فرض آن، یا حتی مدیریت و نظارت بر دیگر پروسه‌های در حال اجرا باشد. فریم‌ورک .NET برای این منظور، یک کلاس بسیار قدرتمند به نام Process را در فضای نام System.Diagnostics فراهم کرده است. در این درس، یاد می‌گیریم که چگونه با استفاده از این کلاس، پروسه‌های جدیدی را شروع کرده، آن‌ها را مدیریت کنیم و با جریان‌های ورودی و خروجی آن‌ها تعامل داشته باشیم.

شروع یک پروسه‌ی جدید

ساده‌ترین راه برای اجرای یک برنامه‌ی دیگر، استفاده از متد استاتیک Process.Start() است. این متد می‌تواند نام فایل اجرایی یا حتی یک آدرس وب را به عنوان ورودی بپذیرد.

Copy Icon Program.cs
using System.Diagnostics;

// Launch Notepad on Windows.
Process.Start("notepad.exe");

// Open a website in the default browser.
Process.Start("https://learnclasico.com");

// Open a folder in File Explorer (with an argument).
Process.Start("explorer.exe", "C:\\Users");

کنترل بیشتر با ProcessStartInfo

برای داشتن کنترل بیشتر بر روی نحوه‌ی اجرای پروسه، باید یک نمونه از کلاس ProcessStartInfo بسازیم. این کلاس به عنوان یک شیء پیکربندی عمل می‌کند و به ما اجازه می‌دهد تا آرگومان‌ها، دایرکتوری کاری و مهم‌تر از همه، نحوه‌ی تعامل با جریان‌های ورودی/خروجی را مشخص کنیم.

Copy Icon Program.cs
var startInfo = new ProcessStartInfo
{
    FileName = "ping",
    Arguments = "google.com -n 10" // Arguments are passed as a single string.
};

Process.Start(startInfo);

مدیریت یک پروسه

متد Process.Start() یک شیء از نوع Process را برمی‌گرداند. این شیء نماینده‌ی پروسه‌ای است که به تازگی ایجاد شده و ما می‌توانیم از آن برای مدیریت و نظارت بر آن پروسه استفاده کنیم.

  • WaitForExit(): اجرای نخ فعلی برنامه را متوقف کرده و تا زمانی که پروسه‌ی خارجی به پایان برسد، منتظر می‌ماند.
  • Kill(): پروسه‌ی خارجی را به زور متوقف می‌کند.
  • ExitCode: کد خروجی پروسه را برمی‌گرداند. طبق قرارداد، کد `0` معمولاً به معنای موفقیت است.
Copy Icon Program.cs
var startInfo = new ProcessStartInfo("ping", "learnclasico.com");

Process myProcess = Process.Start(startInfo);

Console.WriteLine("Waiting for ping process to finish...");
myProcess.WaitForExit();
Console.WriteLine($"Process finished with exit code: {myProcess.ExitCode}");

هدایت مجدد جریان‌های ورودی و خروجی

یکی از قدرتمندترین قابلیت‌های کلاس Process، امکان هدایت مجدد (redirect) جریان‌های استاندارد ورودی، خروجی و خطا است. این به ما اجازه می‌دهد تا خروجی یک ابزار خط فرمان را به جای نمایش در کنسول، در برنامه‌ی C# خود دریافت و پردازش کنیم.

برای این کار، باید چند پراپرتی را در شیء ProcessStartInfo تنظیم کنیم:

  • RedirectStandardOutput = true: به برنامه می‌گوید که جریان خروجی استاندارد را بگیرد.
  • RedirectStandardError = true: به برنامه می‌گوید که جریان خطای استاندارد را بگیرد.
  • UseShellExecute = false: این پراپرتی باید false باشد تا هدایت مجدد کار کند. این یعنی پروسه مستقیماً از فایل اجرایی ایجاد می‌شود، نه از طریق پوسته‌ی سیستم‌عامل (shell).
Copy Icon Program.cs
var startInfo = new ProcessStartInfo("ipconfig")
{
    RedirectStandardOutput = true,
    UseShellExecute = false,
    CreateNoWindow = true // Don't show the command prompt window.
};

Process p = Process.Start(startInfo);

// Read the entire output stream.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();

Console.WriteLine("--- ipconfig output ---");
Console.WriteLine(output);

در این مثال، ما دستور ipconfig را اجرا کرده و تمام خروجی آن را در یک متغیر رشته‌ای به نام output ذخیره می‌کنیم. پراپرتی StandardOutput یک شیء StreamReader است که به ما اجازه می‌دهد خروجی پروسه را مانند یک فایل متنی بخوانیم.

ملاحظات چندپلتفرمی (Cross-Platform)

از آنجایی که .NET چندپلتفرمی است، باید به یاد داشته باشیم که نام فایل‌های اجرایی و دستورات خط فرمان ممکن است بین ویندوز، لینوکس و macOS متفاوت باشد. برای مثال، معادل ipconfig در لینوکس و macOS معمولاً ifconfig یا ip addr است. هنگام نوشتن کدهایی که با پروسه‌ها کار می‌کنند، بهتر است با بررسی سیستم‌عامل، دستور مناسب را انتخاب کنید.

using System.Runtime.InteropServices;

string command;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
    command = "ipconfig";
}
else
{
    command = "ifconfig";
}
Process.Start(command);