ページ 1 / 1
ShellExecute等で実行したプログラムの出力結果を取得したい
Posted: 2012年12月22日(土) 11:51
by MoNoQLoREATOR
コンソールアプリケーションを実行すると、コマンドプロンプトにその結果が出力されますよね。
あれを、ShellExecute等で実行したときに取得したいわけです。
しかし、ファイルに出力してそれを読み込むという方法しかみつかりません。
できれば直接取得したいのですが、どうすればよいのでしょうか。
さらに、ShellExecuteではなくCreateProcessを使用する必要があるという記述も目にしました。
CreateProcessで実行したウインドウを非表示に設定する方法がわからなかったためShellExecuteを採用したのですが、できればウインドウは非表示にしたいですね。
また、実行したいプログラムは自分が作ったものではないということを前提としています。
よろしくお願いいたします。
Re: ShellExecute等で実行したプログラムの出力結果を取得したい
Posted: 2012年12月22日(土) 12:32
by softya(ソフト屋)
CREATE_NO_WINDOWでCreateProcessすれば良いんじゃないでしょうか?
Re: ShellExecute等で実行したプログラムの出力結果を取得したい
Posted: 2012年12月22日(土) 17:35
by ISLe
子プロセスの標準入出力にアクセスする方法は一般的に「パイプ」と呼ばれます。
標準入出力のリダイレクトと説明している場合もあります。
「Win32 コンソール パイプ」というキーワードで検索するとサンプルコードがたくさん見付かると思います。
Re: ShellExecute等で実行したプログラムの出力結果を取得したい
Posted: 2012年12月23日(日) 11:16
by MoNoQLoREATOR
返信ありがとうございます。
>>ソフト屋さん
仰るとおりです。見逃していたようです。
>>ISLeさん
おかげ様でアルゴリズムは掴めました。
管理クラスも作って動作テストをしてみると、しかしCreateProcessの部分でエラーが常に発生してしまいます。
とりあえずCreateProcessを実行するだけのプログラムを書いてみましたが、これすらも動きません。
コード:
int main(){
STARTUPINFO si;
fw::zeromemory(&si);
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
char cmdline[] = "notepad";
BOOL result = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
if(result==0) fw::popup("failed");
else fw::popup("succeeded!");
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
※インクルード文等は省略
fw::zeromemory()というのはZeroMemory()の引数を減少させた自作関数で、
fw::popup()というのはポップアップウインドウを表示するための自作関数です。
コンソール画面のスクリーンショットです。
なにがいけないのでしょうか?
OS:Windows8
IDE:VisualStudio2010
Re: ShellExecute等で実行したプログラムの出力結果を取得したい
Posted: 2012年12月23日(日) 11:37
by softya(ソフト屋)
基本ですが、とりあえずCreateProcessのエラーコードを調べてみては?
Re: ShellExecute等で実行したプログラムの出力結果を取得したい
Posted: 2012年12月23日(日) 14:05
by MoNoQLoREATOR
エラーコードを調べてみました。
「メモリ ロケーションへのアクセスが無効です」
とのことです。
無効な場所にアクセスする可能性があるコードには見えないのですが…。
Re: ShellExecute等で実行したプログラムの出力結果を取得したい
Posted: 2012年12月23日(日) 14:46
by softya(ソフト屋)
zeromemoryにバグがあるんじゃないでしょうか?
私の所では、こうすると動きます。
コード:
#include <windows.h>
#include <stdio.h>
int main() {
static STARTUPINFO si;
si.cb = sizeof( si );
static PROCESS_INFORMATION pi;
char cmdline[] = "notepad";
BOOL result = CreateProcess( NULL, cmdline, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi );
if( result == 0 ) printf( "failed" );
else printf( "succeeded!" );
CloseHandle( pi.hThread );
CloseHandle( pi.hProcess );
}
Re: ShellExecute等で実行したプログラムの出力結果を取得したい
Posted: 2012年12月23日(日) 15:12
by MoNoQLoREATOR
返信ありがとうございます。
siをstaticにすると成功しました。
zeromemoryとpiは特に関係ありませんでした。
コード:
int main(){
static STARTUPINFO si;
fw::zeromemory(&si);
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
char cmdline[] = "notepad";
BOOL result = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
if(result==0){
fw::exerror err;
printf("\nfailed:%s\n", err.get() );
}
else printf("\nsucceeded!\n");
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
※インクルード文等は省略
※fw::exerrorクラスは拡張エラー情報を管理する自作クラス
とりあえずCreateProcessが使えるようになったので、これから管理クラスの動作テストをしていきます。
Re: ShellExecute等で実行したプログラムの出力結果を取得したい
Posted: 2012年12月23日(日) 15:15
by softya(ソフト屋)
zeromemoryってクリアのサイズをどうやって得ているかかなり疑問なんですが、どうやっているのでしょうか? ポインタしか渡していないように見えますが。
Re: ShellExecute等で実行したプログラムの出力結果を取得したい
Posted: 2012年12月23日(日) 17:57
by MoNoQLoREATOR
>>ソフト屋さん
テンプレートを使用して、内部でsizeof演算を行っています。
コード:
template<typename T> void zeromemory(T * req){ ZeroMemory(req, sizeof(T) ); }
というわけで、動作テストを行ってみました。
正常に実行が完了したのですが、結果の文字列を取得することができませんでした。
実行結果のコンソール画面です。
ソースコードです。
► スポイラーを表示
コード:
#define fw_delete(p) delete (p); (p) = NULL;
#define fw_delarr(p) delete [] (p); (p) = NULL;
namespace fw{
template<typename T> void zeromemory(T * req){ ZeroMemory(req, sizeof(T) ); }
class exerror{
private:
char * str;
public:
exerror(){ str = NULL; }
const char * get(){
if(str != NULL){ LocalFree(str); str = NULL; }
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
fw::pointer_cast<LPSTR>(&str),
0,
NULL
);
return str;
}
const char * show(){ return str; }
~exerror(){ if(str != NULL){ LocalFree(str); str = NULL; } }
};
}
class exe{
private:
class Handles{
public:
HANDLE read, write;
Handles(){
read = NULL;
write = NULL;
}
~Handles(){
if(read!=NULL) CloseHandle( read );
if(write!=NULL) CloseHandle( write );
}
};
char* str;
DWORD Timeout;
bool succeeded;
std::string Exepath;
std::string Argus;
fw::thread mythread;
static fw_thread_ GetCUIAppMsgForThread(void * param){
exe & me = *(fw::pointer_cast<exe *>(param) );
me.GetCUIAppMsg();
return 0;
}
public:
exe(){
str = NULL;
Timeout = INFINITE;
}
exe & timeout(const DWORD timeout){
Timeout = timeout;
return *this;
}
exe & exepath(const char * path){
Exepath = fw::cnct()<<'\"'<<path<'\"';
return *this;
}
exe & exepath(const std::string & path){
Exepath = fw::cnct()<<'\"'<<path<'\"';
return *this;
}
exe & _exepath(const char * path){
Exepath = path;
return *this;
}
exe & _exepath(const std::string & path){
Exepath = path;
return *this;
}
exe & argus(const char * path){
Argus = path;
return *this;
}
exe & argus(const std::string & path){
Argus = path;
return *this;
}
void GetCUIAppMsg()
{
succeeded = false;
Handles handles;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = true;
if(!CreatePipe(&(handles.read), &(handles.write), &sa, 0) ){ fw::popup("failed:CreatePipe"); return; }
static STARTUPINFO si;
fw::zeromemory(&si);
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdOutput = handles.write;
si.hStdError = handles.write;
PROCESS_INFORMATION pi;
char * cl = new char[Exepath.length()+1+Argus.length()+1]; // 最初の+1はスペースの分。最後の+1は終端文字の分。
strcpy(cl, Exepath.c_str() );
strcat(cl, " "); // スペースを追加
strcat(cl, Argus.c_str() );
if(CreateProcess(NULL, cl, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)==0){ fw::popup("failed:CreateProcess"); return; }
if(WaitForSingleObject(pi.hProcess, Timeout) != WAIT_OBJECT_0){ fw::popup("failed:WaitForSingleObject"); return; }
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
fw_delarr(cl);
DWORD len;
if(PeekNamedPipe(handles.read, NULL, 0, NULL, &len, NULL)==0){ fw::popup("failed:PeekNamedPipe"); return; }
printf("\nresult length : %d\n", len); // debug
fw_delarr(str);
str = new char[len+1];
str[len] = '\0';
if(len>0 && ReadFile(handles.read, str, len, &len, NULL)==0){ fw::popup("failed:ReadFile"); return; }
succeeded = true;
}
exe & begin(){
mythread.begin(GetCUIAppMsgForThread, this);
return *this;
}
bool working() const { return mythread.working(); }
bool ifsucceeded() const { return succeeded; }
bool iffailed() const { return !ifsucceeded(); }
const char * result() const { return str; }
~exe(){
fw_delarr(str);
}
};
int main(){
exe myexe;
myexe
.exepath("fxc.exe")
.argus("\"C:\\Users\\monoqloreator\\test space\\vs.fx\" /T vs_4_0 /E vs_main /Fh \"C:\\Users\\monoqloreator\\test space\\vs.h\"")
.begin();
while(myexe.working() ){}
if(myexe.ifsucceeded() ) printf("\nsucceeded!\nresult:\n%s\n", myexe.result() );
else{
printf("\nfailed:\n");
fw::exerror err;
printf("%s\n", err.get() );
}
return 0;
}
※インクルード文等は省略
※fw::から始まっているものは自作。ここに定義を載せていないものもある
実行したのはfxc.exeという実行ファイルで、パスは通してあります。
これは Microsoft DirectX11 SDK というSDKに含まれている実行ファイルで、シェーダーの情報が別言語で記述されているファイルをC/C++言語用の.hファイルに変換する実行ファイルです。
この実行ファイルをコマンドプロンプトから実行すると、成功・失敗 どちらの場合でもその旨がコンソールに出力されます。
今回はこれを受け取るプログラムを作りたいわけです。
実行が完了しているのに結果がなしというのは有り得ないはずです。
よって、どこかが間違っているということになるはずです。
しかし全くわかりません。
このような場合はどうやってデバッグすれば良いのでしょうか?
ちなみに、充分予想できたと思いますが、fxc.exeはGUIではないため一々ファイル名等を手動で入力する必要があって、これが面倒だからfxc.exeを間接的に実行するGUIソフトを作ろうと思い立ち、現在に至ります。
Re: ShellExecute等で実行したプログラムの出力結果を取得したい
Posted: 2012年12月23日(日) 18:33
by softya(ソフト屋)
>テンプレートを使用して、内部でsizeof演算を行っています。
了解です。
ただ、siをstaticにすると成功するのが意味不明なんですよね。
とりあえず、もっと単純なコードで試すことと、物事を単純にするために自分で作ったコンソールアプリでデバッグすることでしょうか。
他の人の動いているコードを試してみる必要もあるでしょう。
Re: ShellExecute等で実行したプログラムの出力結果を取得したい
Posted: 2013年1月28日(月) 17:31
by MoNoQLoREATOR
解決しました。
CreateProcessの第5引数をtrueにする必要があったようです。
ありがとうございました。