質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

3回答

4387閲覧

既存アプリケーション標準出力の非同期取得

myuzupa

総合スコア6

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

3クリップ

投稿2018/05/29 06:35

前提・実現したいこと

C#ですでに起動しているコンソールアプリケーションの標準出力を非同期で読み込みたい。
開発環境:Windows10、VS 2017

発生している問題・エラーメッセージ

C#プログラム内部でSystem.Diagnostics.Processクラスを利用して外部アプリケーションを起動して・・・のサンプルや解説はネットで検索して調査できるのですが、前提に記述の通り既に起動しているコンソールアプリケーションの標準出力を取得可能かどうかについては情報がなく・・・
そもそもC#では不可能なのか?すら分かりません。
ご存知の方、よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

既に起動しているコンソールアプリケーションの標準出力を取得可能かどうか」と言うことが興味深かったので、この点について調べてみました。回答としては不完全ですのでご注意ください。

本件について、制限はあるものの、まったくの不可能と言えなくもない方法があります。ただ、その方法はC#/.NET Frameworkのプログラムだけでは無理です。

アイディアとしては CreateRemoteThreadと言うネイティブのWin32 APIを使用し、既に起動しているコンソールアプリケーションに自分のコードを割り込ませ、既に開いている標準出力(STDOUT)のファイルハンドルを切り替えてしまおう、と言うものです。尚、CreateRemoteThreadを使用して別プロセスでコードを実行する手法は、ネイティブのWin32 APIを利用したものとしては良く知られたものです。

参考:
CodeZine - 別のプロセスにコードを割り込ませる3つの方法

イメージとしてはコンソールアプリケーションの中で始動したスレッドがfreopenを実行しているようなものです。
freopen、_wfreope

私の方で試した限りでは、コマンドプロンプト上でprintf等でコンソールに出力しているコンソールアプリケーションに、別のコマンドプロンプト上で実行したプログラムからCreateRemoteThreadでコードを実行させ、ファイルに出力を切り替えさせることができました。ただ、少なくとも以下のような制限事項を認めましたので、実用とするには難しいかもしれません。

  1. 32ビット版、64ビット版で分ける必要がある。既に起動しているコンソールアプリケーションが32ビット版なら、

 割り込ませる方も32ビット版で動作させなければならない。

  1. コンソールアプリケーションが.NET Frameworkのプログラムで、Console.WriteLineのようなコンソール出力用のメソッドを使用している場合、切り替えた瞬間にExceptionが発生する。(Windows 7で発生。Windows 10ではOK)

 突然ファイルハンドルの実体が変わっているので、.NET Frameworkの中で不整合が発生しているのだと思われます。

ハンドルされていない例外: System.IO.IOException: ハンドルが無効です。 場所 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 場所 System.IO.__ConsoleStream.Write(Byte[] buffer, Int32 offset, Int32 count ) 場所 System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder) 場所 System.IO.StreamWriter.Write(Char[] buffer, Int32 index, Int32 count) 場所 System.IO.TextWriter.WriteLine(String value) 場所 System.IO.TextWriter.SyncTextWriter.WriteLine(String value) 場所 System.Console.WriteLine(String value) 場所 ConApp1.Program.Main(String[] args) 場所 D:\project\ConApps\ConApp1\Program.cs:行 23

現状ではまだ不完全なので、実験したコードは載せないことにしました。完成度が上がったら参考程度にgithubにあげるかもしれません。その際は抜粋で本回答に追記したいと思います。(勿体付けるほどのコードではありません。。。)

投稿2018/05/30 13:18

dodox86

総合スコア9183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

myuzupa

2018/06/01 01:18

ご回答ありがとうございます。プロジェクトの規模的にご提示いただいた方法は実装が厳しそうですので別のアプローチを含めてもう少し模索してみます。ご提供いただいた情報を含めて内部で検討させていただきます。
dodox86

2018/06/01 13:51

私の回答はどちらかと言うと「できるかできないか」と言う技術的興味から、という部分も大きいので、あまり気にしないでください。私も一般的な製品でこの案を採用するかと言えば、安全性も考えて採用しない可能性が高いです。
guest

0

こんにちは。

はじめに

プロセスのデバッグイベントを監視することで、出力を補足することができます。
ただしネットで検索してもC++の情報が多くヒットし、質問者様のようにC#でやるには少しテクニックが必要です。

プロセスデバッガを作ってみる

上記のサイトではVC++でプロセスデバッガの作り方を解説しています。

どうするか

これらをC#で実現するシンプルな方法は、先のチュートリアルで示されたC++のコードをC#に置き換えてしまうことです。
WIN32 APIの関数をDllImportを使ってC#からコールします。

例えば...

csharp

1[DllImport("kernel32.dll", EntryPoint = "WaitForDebugEvent", CallingConvention = CallingConvention.StdCall)] 2[return: MarshalAs(UnmanagedType.Bool)] 3public static extern bool WaitForDebugEvent([In,Out] ref DEBUG_EVENT lpDebugEvent, uint dwMilliseconds);

このような感じで使えます。
C++の関数をC#で使いたい場合はPInvoke.netが便利です。

誤解なきように申し上げると、全部の関数をDllImportしてください、という話ではありません。
C#にない(または実現するのが難しい)ものに対して使ってください。

最後に

具体的なコード例を示さず申し訳ありません。コード量が多くなるのご了承ください。
本件、ニッチなことで情報が少ないのはよくわかります。
チュートリアルにあるような関数でネット検索すると類似の情報がヒットするかもしれませんので、もしよければ調べてみてください。

また、ここで示した話は少し古い情報です。昨今のバージョンではもっと別のアプローチがあるかもしれません。

全く別のアイディア

ここまでの話とは別に、シンプルなアイディアとして、監視先のプロセスからテキストファイルに出力を行い、監視元のプロセスはそのテキストファイルの変更を監視するというはどうでしょう。
これだとC#の標準的な機能で実現できそうです。

投稿2018/05/30 01:19

g_uo

総合スコア212

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

myuzupa

2018/06/01 01:18

ご丁寧な回答ありがとうございます。「全く別のアイディア」として頂いていたアプローチは検討中だったのですが、プロジェクトの規模的にそちらでの解決を考えるのが現実的かもしれません。この件では更に調べていますので、また追加の情報がわかりましたら書き込みします。
g_uo

2018/06/01 01:37

コメントありがとうございます。プロセス間の制御は様々なトラブルを生むことがあり、とくに.Netから外れた場合は問題の解決が難しくなると思います。 そのため別案のほうでクリアできるのであればそれが好ましいと思います。ただそちらについては似たような仕様の実装を行った経験がありませんので、詳細なフォローは難しいです。無責任な回答で申し訳ありません。 また表題の問題について何かあればよろしくお願いします。
dodox86

2018/06/01 13:46

監視先のプログラムのコードを修正できるのであれば、私もg_uoさんの代案を推します。 ファイルをcloseせずに書き込み続けたり、flushしなかったり、ファイルの共有モードの設定が 不適切だったりすると、排他の関係で書き込みに支障が出たり、監視側が読めなかったりするケースがあるので、気に留めてみてください。
guest

0

要求水準が分かりませんが、UIAutomationを使用して画面情報から文字列を取得する方法でかろうじて取得することはできます。

しかし、この方法だとポーリングやイベント等で監視して定期的に取得する必要があるのと
コンソールのバッファをオーバーして消えてしまった文字列は取れません。
APIフックを使用する場合でも、すでに出力されてしまった文字列は取得できません。

どう考えてもコンソールアプリを直接起動するかコンソールアプリ側を改修して別の手段で受け渡したほうがいいと思います。

一応、以下がUIAutomationを使用したサンプルコードです。
cmd.exeを起動してから実行してください。

参照に
UIAutomationClient
UIAutomationTypes
を追加

C#

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Text; 5using System.Threading.Tasks; 6using System.Windows.Automation; 7 8namespace ConsoleReader 9{ 10 class Program 11 { 12 [MTAThread] 13 static void Main(string[] args) 14 { 15 var console = AutomationElement.RootElement.FindFirst(TreeScope.Children, 16 new PropertyCondition( 17 AutomationElement.NameProperty, 18 //ここはWindowのタイトルなので適当に変える 19 @"C:\WINDOWS\system32\cmd.exe")); 20 21 if (console == null) return; 22 23 var textarea = console.FindFirst(TreeScope.Descendants, 24 new PropertyCondition( 25 AutomationElement.ControlTypeProperty, ControlType.Document)); 26 27 if (textarea == null) return; 28 29 var text = 30 textarea.GetCurrentPattern(TextPattern.Pattern) as TextPattern; 31 32 if (text == null) return; 33 34 var ranges = text.GetVisibleRanges(); 35 if (ranges == null) return; 36 if (ranges.Length == 0) return; 37 foreach(var range in ranges) 38 { 39 string line = range.GetText(-1);//日本語の部分の取得はおかしくて2重に取得される?(Win10 RS4 x64) 40 Console.WriteLine(line); 41 } 42 Console.ReadLine(); 43 44 } 45 } 46} 47

投稿2018/06/01 15:38

編集2018/06/01 15:39
hmmm

総合スコア818

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問