7

System.Diagnostics.Processは、StandardInputという名前のStreamWriterを公開します。これは、私が知る限り、文字のみを受け入れます。

ただし、キーストロークも送信する必要があり、一部のキーストロークは文字に適切にマッピングされません。

私は何をすべきか?

4

4 に答える 4

29

入力ストリームを制御信号と混合しています。すでにご存知のように、コンソールプロセスには、StandardInputで制御できるデフォルトの入力ストリームがあります。ただし、Ctrl-CおよびCtrl-Breakは、このストリームを介してプロセスに送信される文字ではなく、代わりに、登録されたシグナルハンドラーを使用してプロセスが受信する制御シグナルです。CTRL+CおよびCTRL+BREAKシグナルを参照してください。

デフォルトでは、コンソールウィンドウにキーボードフォーカスがある場合、CTRL+CまたはCTRL+BREAKは、キーボード入力としてではなく、信号(SIGINTまたはSIGBREAK)として扱われます。

偽の信号をプロセスに送信するには、またはのいずれかを使用しGenerateConsoleCtrlEventて送信できます。このAPIには.Netに相当するものがないため、PInvokeする必要があります。CTRL_C_EVENTCTRL_BREAK_EVENT

.NETから使用するには、関数定義を含める必要があります。

const int CTRL_C_EVENT = 0;
const int CTRL_BREAK_EVENT = 1;

[DllImport("kernel32.dll")]
static extern bool GenerateConsoleCtrlEvent(
    uint dwCtrlEvent,
    uint dwProcessGroupId);
于 2010-02-20T21:06:45.390 に答える
6

ここCodeplexに入力シミュレーターがあります。これはあなたにぴったりの仕事をするかもしれません。私はサンプルコードに取り組んでおり、まもなくここに投稿します。入力シミュレーターは、R​​emusが提供するリンクで見つかったものと似ていることに注意してください...

編集:System.Windows.Forms.SendKeys.Sendこれには制限があることがわかりました。通常の方法で確実に回避できます。効果的に機能します。、しかし、プロセスは持っている必要があります

  • ストリームのリダイレクトはありません
  • ウィンドウを非表示にすることはできません(ウィンドウのハンドルがどこにも表示されないため、これは失敗します。ウィンドウを前面に移動してアクティブにする方法はありません!)
  • これが効果的であるためのプロセスを示すウィンドウ!

あなたの場合、それはウィンドウを見つけて、pinvoke'SetForegroundWindow'を介してアクティブに設定し^{BREAK}、Ctrl + Break信号を送信するシーケンスを非常にうまく機能するプロセスに送信することです(特にプロセスがコマンドラインプログラムの場合) /バッチファイル)。これは、これを正確に実行し、SendKeysを反映するCodeProjectに関する記事です...これを示すために、まだいくつかのコードを貼り付けていません...。

編集#2:実際、私は非常に驚いています...このコードが示すように(概念実証)...使用しています:

  • InputSimulator(前述のとおり)
  • ボタンで構成されるWindowsフォーム。フォームが読み込まれると、クラスが自動的に実行されます。ボタンをクリックすると、隠されたプロセスにctrl-breakを投稿します
  • 出力ストリームは実際にリダイレクトされ、非表示のウィンドウです。
  • 奇妙なことに、出力はキャプチャされていますが、デバッグウィンドウに結果が表示されません。つまり、プロセスが終了するまでバッファリングされ(おそらく)、出力全体が表示されます...
  • FindWindowウィンドウのタイトルが前面に表示され、InputSimulatorを使用してキーストロークを送信できること、または従来の単純な古いSendKeys関数を使用できることを知っていたので、API呼び出しを少しだましました。 。私が持っていた理由Thread.Sleepは、「アクティブなフォアグラウンドウィンドウ」のキーボードキューにプッシュされるためにキーストロークが送信されることを確認するためです。
  • 「netstat-e5」コマンドを使用して永久ループを作成し、「Ctrl + C」を受信して​​無限ループを解除するまで、5秒ごとに結果を更新しました。
public partial class Form1 : Form
{
    private TestNetStat netStat = new TestNetStat();
    public Form1()
    {
       InitializeComponent();
       using (BackgroundWorker bgWorker = new BackgroundWorker())
       {
           bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
           bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
           bgWorker.RunWorkerAsync();
       }
    }

    void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
       System.Diagnostics.Debug.WriteLine("BGWORKER ENDED!");
    }

    private void  bgWorker_DoWork(object sender, DoWorkEventArgs e)
    {
       netStat.Run();
    } 
    void btnPost_Click(object sender, EventArgs e)
    {
       netStat.PostCtrlC();
       System.Diagnostics.Debug.WriteLine(string.Format("[{0}] - {1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), this.netStat.OutputData.Replace(Environment.NewLine, "")));
    }
}

public class TestNetStat
{
    private StringBuilder sbRedirectedOutput = new StringBuilder();
    //
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    [DllImport("user32")]
    public static extern int SetForegroundWindow(IntPtr hwnd);
    public string OutputData
    {
       get { return this.sbRedirectedOutput.ToString(); }
    }
    public void PostCtrlC()
    {
       IntPtr ptr = FindWindow(null, @"C:\Windows\System32\netstat.exe");
       if (ptr != null)
       {
          SetForegroundWindow(ptr);
          Thread.Sleep(1000);
          WindowsInput.InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.CONTROL, VirtualKeyCode.CANCEL);
          // SendKeys.Send("^{BREAK}");
          Thread.Sleep(1000);
        }
    }
    public void Run()
    {
        System.Diagnostics.ProcessStartInfo ps = new System.Diagnostics.ProcessStartInfo();
        ps.FileName = "netstat";
        ps.ErrorDialog = false;
        ps.Arguments = "-e 5";
        ps.CreateNoWindow = true;
        ps.UseShellExecute = false;
        ps.RedirectStandardOutput = true;
        ps.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
        using (System.Diagnostics.Process proc = new System.Diagnostics.Process())
        {
           proc.StartInfo = ps;
           proc.EnableRaisingEvents = true;
           proc.Exited += new EventHandler(proc_Exited);
           proc.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(proc_OutputDataReceived);
           proc.Start();
           proc.BeginOutputReadLine();
           proc.WaitForExit();
        }
     }

     void proc_Exited(object sender, EventArgs e)
     {
        System.Diagnostics.Debug.WriteLine("proc_Exited: Process Ended");
     }

     void proc_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
     {
         if (e.Data != null)
         {
            this.sbRedirectedOutput.Append(e.Data + Environment.NewLine);
            System.Diagnostics.Debug.WriteLine("proc_OutputDataReceived: Data: " + e.Data);
         }
     }
}

Nitpickyはさておき、が「BackgroundWorker」スレッドから実行されていることを知っていnetStatます。メインのGUIスレッドから「PostCtrlC」メソッドを直接呼び出しました...これは概念実証コードとしては衒学的ですが、それはスレッドセーフにするために「ISynchronizeInvoke」を実装する必要がありますが、それはさておき...実際に機能します。

于 2010-02-21T01:50:04.027 に答える
3

この素晴らしいツール-AutoItを見たことがありますか。これはスクリプトツールです。バックスペースを送信するには、Send("{BACKSPACE}")

これは優れたツールであり、多くの手動クリック/ダブルクリックなどを自動化するのに役立ちます。

これはあなたの質問に関連していますか?

于 2010-02-17T10:45:34.567 に答える
0

キーを送信できるWindowsフォームウィンドウがある場合は、SendKeysが適切なソリューションである可能性があります。

バックスペースとCtrl+Cを押す場合は、次のようになります。

SendKeys.Send("{BACKSPACE}^C");
于 2010-02-20T20:54:09.507 に答える