標準出力のリダイレクト

ActiveBasicでのプログラミングでわからないこと、困ったことなどがあったら、ここで質問してみましょう(質問を行う場合は、過去ログやWeb上であらかじめ問題を整理するようにしましょう☆)。
返信する
メッセージ
作成者
tomo

標準出力のリダイレクト

#1 投稿記事 by tomo »

はじめまして。tomoと申します。

GUIを持つアプリケーションから、コンソールアプリケーションを呼び出す際に、
結果を(途中経過をリアルタイムで)コンソールではなくGUI中のテキストボックス等に
表示させたいのです。
(コンソールは表示させない方向で・・・)

調べてみたところ、「CreateProcess を使用し、STARTUPINFO構造体の
dwFlags と hStdOutput を設定しておく」まではわかったのですが、
いざコードを書いてみると、目的のコンソールアプリケーションは実行されているようですが、
結果を表示させることができませんでした。

コード: 全て選択

Dim CmdLine As String
Dim si As STARTUPINFO
Dim pi As PROCESS_INFORMATION

CmdLine = "cmd.exe /c dir c:\windows\system32"

si.cb = Len(si)
si.dwFlags = STARTF_USESTDHANDLES
si.hStdOutput = GetDlgItem(hMainWnd,EditBox1)

If CreateProcess(0, StrPtr(CmdLine), ByVal 0, ByVal 0, TRUE, DETACHED_PROCESS, 0, 0, si, pi) Then
    CloseHandle(pi.hProcess)
    CloseHandle(pi.hThread)
End If
どのようにすればよいのでしょうか…
どうかお力添えをいただけませんか?よろしくお願いします。
7
記事: 473
登録日時: 2005年5月31日(火) 18:51
お住まい: 新潟県
連絡する:

Re: 標準出力のリダイレクト

#2 投稿記事 by 7 »

> いざコードを書いてみると、目的のコンソールアプリケーションは実行されているようですが、
> 結果を表示させることができませんでした。
件のコードではたぶん目的のアプリケーションすら実行されていないと思います。
自分もいま実験しているのですが対象のコントロールに出力結果を表示することは出来ていません...。

とりあえず目的のアプリケーションを実行する所までは出来ました。

コード: 全て選択

Dim si As STARTUPINFO
Dim pi As PROCESS_INFORMATION

	si.cb=SizeOf(STARTUPINFO)
	' ウィンドウを非表示で実行
'	si.dwFlags=STARTF_USESHOWWINDOW
'	si.wShowWindow=SW_HIDE

	If CreateProcess(
			"C:\WINDOWS\system32\cmd.exe", ' 実行ファイルへのパス
			"/c dir c:\windows\system32", ' 実行ファイルに渡すコマンドライン文字列
			ByVal NULL,ByVal NULL,TRUE,NORMAL_PRIORITY_CLASS,0,
			"c:\windows\system32", ' カレントディレクトリ
			si,pi) Then
'		WaitForSingleObject(pi.hProcess,INFINITE)
		CloseHandle(pi.hThread)
		CloseHandle(pi.hProcess)
	End If
追記。
CreatePipe()関数やPeekNamedPipe()関数を使う必要があるようです。

コード: 全て選択

Declare Function CreatePipe Lib "kernel32.dll" (
	ByVal hReadPipe As *HANDLE,
	ByVal hWritePipe As *HANDLE,
	ByRef lpPipeAttributes As SECURITY_ATTRIBUTES,
	ByVal nSize As DWord) As BOOL

Declare Function PeekNamedPipe Lib "kernel32.dll" (
	ByVal hNamedPipe As HANDLE,
	ByVal lpBuffer As VoidPtr,
	ByVal nBufferSize As DWord,
	ByVal lpBytesRead As *DWord,
	ByVal lpTotalBytesAvail As *DWord,
	ByVal lpBytesLeftThisMessage As *DWord) As BOOL
とりあえずエディットボックスに出力結果の一部を表示することは出来たのですが、ちょっとこれは正しい動作じゃないような...。
tomo

Re: 標準出力のリダイレクト

#3 投稿記事 by tomo »

7様、お返事ありがとうございます。

> 件のコードではたぶん目的のアプリケーションすら実行されていないと思います。

ありゃ…たしかに挙動が違いますね。私のコードでは実行されていないようです。実は試しに、私のコードを

コード: 全て選択

CmdLine = "cmd.exe /c dir c:\windows\system32 > c:\a.txt"
に変更して実行すると、a.txt が生成されていたので(ちゃんと中身あり)、実行できているとばかり思っていました…(汗)


> とりあえず目的のアプリケーションを実行する所までは出来ました。

コード: 全て選択

> Dim si As STARTUPINFO
> Dim pi As PROCESS_INFORMATION
> 
> 	si.cb=SizeOf(STARTUPINFO)
> 	' ウィンドウを非表示で実行
> '	si.dwFlags=STARTF_USESHOWWINDOW
> '	si.wShowWindow=SW_HIDE
> 
> 	If CreateProcess(
> 			"C:\WINDOWS\system32\cmd.exe", ' 実行ファイルへのパス
> 			"/c dir c:\windows\system32", ' 実行ファイルに渡すコマンドライン文字列
> 			ByVal NULL,ByVal NULL,TRUE,NORMAL_PRIORITY_CLASS,0,
> 			"c:\windows\system32", ' カレントディレクトリ
> 			si,pi) Then
> '		WaitForSingleObject(pi.hProcess,INFINITE)
> 		CloseHandle(pi.hThread)
> 		CloseHandle(pi.hProcess)
> 	End If
wShowWindow というものがあったのですね。過去ログで、コンソールを表示しないには、DETACHED_PROCESS が使えると
書いてあったのでそれを使わせていただいていました^^;

> 追記。
> CreatePipe()関数やPeekNamedPipe()関数を使う必要があるようです。

ありゃ…いきなり難しい話になってしまいましたな…(滝汗)
さすがにエディットボックスに直に表示はできませんか…
(出力垂れ流しなはずだし、そのまま表示くらい…と思っていたのですが。甘かったですな…)

> とりあえずエディットボックスに出力結果の一部を表示することは出来たのですが、ちょっとこれは正しい動作じゃないような...。

おぉ、すごいです!形になれば、是非伝授してください^^;
7
記事: 473
登録日時: 2005年5月31日(火) 18:51
お住まい: 新潟県
連絡する:

Re: 標準出力のリダイレクト

#4 投稿記事 by 7 »

> 実行できているとばかり思っていました…(汗)
あ。よく分かんないんですけど、そっちのでも出来るみたいです...。
ネットに落ちてるサンプルコードを見てみるとtomoさんのコードになっているのもありました。

> さすがにエディットボックスに直に表示はできませんか…
> (出力垂れ流しなはずだし、そのまま表示くらい…と思っていたのですが。甘かったですな…)
コンソールアプリケーションが流すデータをパイプを通して奪い取り、それを変数に確保、そのデータをエディットボックスに出力、といった形になるようです。

いまはまだちゃんと実装できていないのですが、以下のサンプルを参考に一応頑張っています...。
誰かサクっと実装できる方いませんか?
tak
記事: 162
登録日時: 2005年5月31日(火) 07:49

Re: 標準出力のリダイレクト

#5 投稿記事 by tak »

7さんの提示してくださったサンプルコードを参考にして、とりあえず動くものをこさえてみました。
Ver 4.24 でのみ動作検証を行いました。これ以降の Ver5CP 版では多少手直しが必要かもしれませんが、たぶん深刻な問題は発生しないと思います。
> > さすがにエディットボックスに直に表示はできませんか…
> > (出力垂れ流しなはずだし、そのまま表示くらい…と思っていたのですが。甘かったですな…)
> コンソールアプリケーションが流すデータをパイプを通して奪い取り、それを変数に確保、そのデータをエディットボックスに出力、といった形になるようです。

はい。7さんの指摘されているとおりで、パイプを通して入力されたテキストをバッファに溜めておき、それを SetWindowText() などでエディットボックスに表示させるのがもっとも簡単だと思います。
tomo

#6 投稿記事 by tomo »

7様、tak様、お返事ありがとうございます。

ちゃんと標準出力を取得できていますね~。感動です><
本当にありがとうございました。
いやはや、やってる事が高度すぎて、コピーして少し手を加えるのが精一杯…精進します。(ソースのコメントには大いに助けられております…)
それにしても、取得するだけでこんなにも複雑な手順を踏まないといけないとは…。
#そのうち専用の関数とか出たらいいのに…(汗)

今はテキストボックスに「リアルタイムに」表示できるよう奮闘中です^^;
(While-Wend中でバッファをを別変数に追加・追加…→ループ外でSetDlgItemTextを使い、一括表示は可能でした。
ただ、SetDlgItemTextをループ内に入れてしまうと、毎回全テキストを書き換えるためか、固まってしまい撃沈…
現在過去ログを頼りに、テキストボックスに追記していく方法を模索中です)

今回は本当にありがとうございました。
ゲスト

#7 投稿記事 by ゲスト »

http://www.2chab.net/uploader/src/up0021.zip
上の、349行目の

コード: 全て選択

SendMessage(GetDlgItem(hMainWnd,EditBox2),194,0,buf2)
って奴を少し書き換えれば「追記」することはそんなに難しくはなさそうですよ。
7
記事: 473
登録日時: 2005年5月31日(火) 18:51
お住まい: 新潟県
連絡する:

#8 投稿記事 by 7 »

> 現在過去ログを頼りに、テキストボックスに追記していく方法を模索中です)
エディットボックスにデータを追加していく関数を以前何回も投稿しました(笑)
見つけることが出来たかもしれませんが、一応また投稿しておきます。

コード: 全て選択

' エディットボックスに文字列を追加
Function Edit_AddText(ByVal hWnd As HWND,ByVal lpString As LPSTR,ByVal bUndo As BOOL) As BOOL
    SendMessage(hWnd,EM_SETSEL,GetWindowTextLength(hWnd),-1)
    Edit_AddText=SendMessage(hWnd,EM_REPLACESEL,bUndo,lpString As LPARAM) As BOOL
End Function
lpStringに追加したい文字列、bUndoTRUEを指定すると「元に戻す」を実行できます。
返信する