簡體   English   中英

C#:如何開啟一個進程,等待進程中的程序加載完成,然后使用新的進程執行命令?

[英]C#: How can I open a process, wait until the program in the process loads completely, and then use a new process to execute commands?

對於 C#,我需要打開一個程序,等到該程序完全加載,然后我想運行 cmd.exe 來運行其他命令來操作該程序的原始實例。

我試圖讓一個名為 GerbView 的特定程序以某種方式運行。 該程序用於查看 Gerber 文件,PCB 制造商用來蝕刻 PCB 的文件。 該程序可以使用自己的打開命令在內部加載 Gerber 文件。 然而,我的程序是為了處理其中一些 Gerber 文件,然后使用 GerbView 加載這些處理過的 Gerber 文件。 我能夠完成此任務的唯一方法是通過命令行界面。 可以打開 Gerber 文件的命令是gerbview {filename}

我知道進程 class 可以使用 FileName 和 Arguments 屬性輕松完成此操作,但我需要向同一實例添加更多文件。 執行此操作的命令是gerbview /add {filename}

最后一條命令將啟動程序,如果一個實例已經打開,它只會添加一個額外的文件以供查看。 現在這是我遇到問題的地方。 在我提出最終解決方案(見下文)之前,我的程序一直打開多個實例(盡管使用了/add參數),這可能是因為在執行/add arguments 的 rest 時程序尚未准備好輸入。 只有當我添加 1 秒延遲時,代碼才能正確運行。 但是,當我通過 Microsoft Teams 向某人展示時,舊行為又回來了,直到我增加了延遲(我假設 Teams 廣播導致需要更大的延遲)。 以下是我目前的解決方案(該程序是 windows 表格的一部分):

Process gerbView;
Process cmd;
bool gerbViewActive;

// The is the windows form constructor
public WindowsFormGUI()
{
    InitializeComponent();
    gerbViewActive = false;
}

private void buttonRunProgram_Click(object sender, EventArgs e)
{
    OpenFileDialog loadFiles = new OpenFileDialog
    {
        Multiselect = true,
        InitialDirectory = Directory.GetParent(Directory.GetCurrentDirectory()).FullName
    };

    if (loadFiles.ShowDialog() == DialogResult.OK)
    {
        if (!gerbViewActive)
        {
            gerbView = new Process();
            gerbView.EnableRaisingEvents = true;
            gerbView.Exited += new EventHandler(GerbView_Exited);
            gerbView.StartInfo.FileName = @"C:\Program Files\Software Companions\GerbView\gerbview.exe";
            gerbView.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
            gerbView.Start();
            gerbViewActive = true;
        }
        
        // wait until GerbView loads before running the cmd process.
        Thread.Sleep(2000);
        
        string[] files = loadFiles.FileNames;
        cmd = new Process();
        cmd.StartInfo.FileName = "cmd.exe";
        cmd.StartInfo.RedirectStandardInput = true;
        cmd.StartInfo.UseShellExecute = false;
        cmd.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
        cmd.StartInfo.WorkingDirectory = @"C:\Program Files\Software Companions\GerbView\";
        cmd.Start();
        using (StreamWriter cmdInput = cmd.StandardInput)
        {
            for (int index = 0; index < files.Length; index++)
            {
                cmdInput.WriteLine("gerbview.exe /add " + files[index]);
            }
        }
        cmd.Close();
    }
}

private void GerbView_Exited(object sender, EventArgs e)
{
    gerbViewActive = false;
    gerbView.Close();
}

任何幫助或建議將不勝感激。

首先,您正在執行的命令行工具必須嘗試定位現有進程並與之通信。 我會跳過運行cmd.exe ,而只是自己運行每個gerbview.exe /add進程。 然后你可以.WaitForExit()來確保每個進程在開始另一個進程之前已經完成。 然而,這可能還不夠。

接下來,您需要在主 UI 進程上使用.WaitForInputIdle()進行阻塞,直到主線程清除了它的 win32 事件隊列。 這很可能是他們的文件打開命令行能夠與之成功通信的必要條件。

明確地說,您需要的最少步驟是;

  • 啟動主進程“gerbview.exe”,然后遍歷文件;
    • 主進程.WaitForInputIdle
    • 使用 arguments $"/add "{filename}"" 啟動 "gerbview.exe"
    • .WaitForExit 這個“gerbview.exe”進程

由於您正在啟動與主 UI 相同的查看器可執行文件並打開每個文件,因此它很可能是“windows 子系統”可執行文件而不是控制台可執行文件。 cmd.exe 將啟動進程,但在退出之前不會阻塞。 確定它已退出的唯一方法是自己啟動該過程。

您還可以嘗試向查看器進程的 window 句柄發布或發送一些 noop window 消息,以確保.WaitForInputIdle 有一些新的輸入要等待。

這些都不能保證這個過程會起作用。 這完全取決於這個第 3 方應用程序的編寫方式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM