EternalWindows
コンソール / 標準入出力とリダイレクト

WinMainから始まるWindowsアプリケーションを開発する一番の理由は、自作のウインドウを表示できるという点に尽きるでしょう。 mainから始まるコンソールアプリケーションでは起動と共にコンソールが表示されますから、 ユーザーがグラフィカルな操作を行えなくなるという欠点が目立ちます。 しかし、グラフィカルなWindowsアプリケーションであっても、場合によってはコンソールを表示したいことがあるかもしれません。 たとえば、デバッグ時などでデータを視覚的に確認したい場合はコンソールが役に立ちますから、 コンソールを表示するための関数を理解しておく意味はあります。 もちろん、単にコンソールを表示するだけのWindowsアプリケーションを開発するならば、 最初からコンソールアプリケーションとして開発を行うべきといえます。

Windowsアプリケーションからコンソールを表示する方法は次節で説明するとして、 今回はコンソールと標準入出力の関係について説明したいと思います。 まずは、次のコードを見てください。

#include <stdio.h>

void main()
{
	printf("Hello World\n");
}

このコードを実行することで何が起きるかは、改めて問う必要もないと思われます。 単純に考えて、Hello Worldという文字列が標準出力に対して出力されることになります。 コンソールに対して出力されると答えることもできますが、これも一応間違いではありません。 確かに特別な値を指定せずコンソールアプリケーションを起動した場合は、 標準出力としてコンソールが割り当てられますから、 printfがコンソールへの出力として機能することになるのは事実です。 また、このような場合は標準入力としてキーボードが割り当てられますから、 標準入力から文字列を読み取るgetsはキーボードからの入力を受け取ることになります。

printfがコンソールではなく標準出力に対して出力を行うという点に注目すれば、 ある1つの面白い案が浮かびます。 それは、標準出力として外部アプリケーションが作成したハンドルを割り当てれば、 printfによる出力を外部アプリケーションが検出できるというものです。 たとえば、標準出力としてファイルハンドルを割り当てれば、 printfに指定した文字列はファイルに出力されるようになります。 このように、本来はコンソールに出力されるべき内容を、 別のハンドルに対して出力されるように切り替える方法をリダイレクトと呼びます。

コンソールへの出力をリダイレクトするためには、リダイレクト先のハンドルを対象のプロセスに割り当てなければなりません。 プロセスを起動するCreateProcessはこのような動作をサポートするため、その使い方を見てみたいと思います。

#include <windows.h>

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	STARTUPINFO         startupInfo;
	PROCESS_INFORMATION processInformation;
	HANDLE              hFileOut;
	HANDLE              hStdOutput;

	hFileOut = CreateFile(TEXT("sample.txt"), GENERIC_WRITE, 0,  NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	DuplicateHandle(GetCurrentProcess(), hFileOut, GetCurrentProcess(), &hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);

	ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
	startupInfo.cb         = sizeof(STARTUPINFO);
	startupInfo.dwFlags    = STARTF_USESTDHANDLES;
	startupInfo.hStdOutput = hStdOutput;

	CreateProcess(TEXT("C:\\Windows\\System32\\ipconfig.exe"), NULL, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &startupInfo, &processInformation);

	MessageBox(NULL, TEXT("終了します。"), TEXT("OK"), MB_OK);

	CloseHandle(hFileOut);
	CloseHandle(hStdOutput);
	CloseHandle(processInformation.hThread);
	CloseHandle(processInformation.hProcess);

	return 0;
}

まず、CreateFileでリダイレクト先とするハンドルを取得します。 次に、これをプロセスが継承できるようにDuplicateHandleで複製し、 STARTUPINFO構造体のhStdOutputメンバに指定します。 このメンバは標準出力のハンドルを指定することができ、初期化する場合はdwFlagsにSTARTF_USESTDHANDLESを指定します。 CreateProcessの第5引数にTRUEを指定すれば、ハンドルは作成したプロセスに継承されることになります。 第6引数にCREATE_NO_WINDOWを指定しているのは、作成したプロセスでコンソールが表示されることを防ぐためです。 コンソールへの入力をリダイレクトするということは、コンソールに何も表示されなくなることを意味しますから、 最初から表示されないようにしておくわけです。 上記コードでは起動するプロセスがipconfig.exeになっているため、 このプロセスが出力する内容がファイルに反映されることになります。 言い換えれば、コマンドプロントにipconfigと入力した内容をファイルを通じて確認できるようになります。


戻る