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

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

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

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

5回答

12617閲覧

GUI と CUI の共存

BeatStar

総合スコア4958

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

4クリップ

投稿2017/05/20 03:50

編集2017/05/20 04:45

C/C++ ( Windows API 実装 ) でやっています。

GUIとCUIって共存できるのでしょうか?

[定義] ここでいう、GUIは Windows API だと EDIT, RADIO, LISTのようなコントロール, VBA とかだと TextBox, ListBox, RadiButton みたいなコントロール, Javaだと コンポーネントを表示できる、ウィンドウ ( ユーザーフォームとも言われたりするが。 ) のことです。 正式名称は Graphical User Interface らしいです。 CUIは Character User Interface らしいです。 別名? コンソール。

Firefoxは微妙ですが、コマンドライン引数を受け取って,

コマンドライン引数 が "/h" みたいに「ヘルプを表示せよ」のようなオプションのとき、

コマンドライン ( コマンドプロンプト, ターミナル etc. ) に出力しますよね。

あれって、どうやっているのでしょうか?

本体は GUI なのに コマンドラインに表示可能...

どういう風にやっているのでしょうか。

調べてみると「EternalWindows」( http://eternalwindows.jp/ ) がヒットしました。

"猫でもできるプログラミング" もヒットしました。

この EternalWindowsさんで説明されている内容を私なりにクラス化してみました。

ですが、思うように動きません。

動くことは動くのですが、

Windows で言えば STARTコマンドでやったような状態になります。

一応のソースコード:

C++

1// CConsole.h 2 3//---------------------------------------------- 4// インクルードガード BEGIN 5//---------------------------------------------- 6#ifndef CCONSOLE_H 7#define CCONSOLE_H 8 9 10 11//---------------------------------------------- 12// インクルードファイル 13//---------------------------------------------- 14#include<string> 15#include<windows.h> 16 17 18 19//---------------------------------------------- 20// プロトタイプ/クラス宣言 21//---------------------------------------------- 22 23class CConsole{ 24 public: 25 CConsole() throw(int); 26 ~CConsole(); 27 28 bool SetColor( COLORREF foreColor, COLORREF bgColor = -1 ); 29 bool GetColor( COLORREF &foreColor, COLORREF &bgColor ); 30 bool Write( const std::string str ); 31 bool Read( std::string &result ); 32 void Wait( void ); 33 private: 34 HANDLE hOutput; 35 HANDLE hInput; 36 CONSOLE_SCREEN_BUFFER_INFO csbi; 37}; 38 39#endif

C++

1// CConsole.cpp 2 3#include"CConsole.h" 4 5//---------------------------------------------- 6// namespace 7//---------------------------------------------- 8using namespace std; 9 10 11/************************************************************** 12* 13* 参 ・ 引: 14* 「EternalWindows」( http://eternalwindows.jp/ ) 15* ->「Windows開発」 16* ->「コンソール」 17* ->「コンソールへの入出力」 18* 19* 「猫でもわかるプログラミング」( http://www.kumei.ne.jp/c_lang/ ) 20* ->「C言語編 第1部」 21* ->「コンソールアプリで文字色を変える」 22* 23**************************************************************/ 24 25CConsole::CConsole() throw(int){ 26 if( AllocConsole() == 0 ) throw -1; 27 28 hOutput = GetStdHandle( STD_OUTPUT_HANDLE ); 29 hInput = GetStdHandle( STD_INPUT_HANDLE ); 30 31 GetConsoleScreenBufferInfo( hOutput, &csbi ); 32} 33 34 35 36/************************************************************** 37* 38* 参 ・ 引: 39* 「EternalWindows」( http://eternalwindows.jp/ ) 40* ->「Windows開発」 41* ->「コンソール」 42* ->「コンソールへの入出力」 43* 44* 「猫でもわかるプログラミング」( http://www.kumei.ne.jp/c_lang/ ) 45* ->「C言語編 第1部」 46* ->「コンソールアプリで文字色を変える」 47* 48**************************************************************/ 49 50CConsole::~CConsole(){ 51 FreeConsole(); 52} 53 54 55 56/************************************************************** 57* 58* 参 ・ 引: 59* 「EternalWindows」( http://eternalwindows.jp/ ) 60* ->「Windows開発」 61* ->「コンソール」 62* ->「コンソールへの入出力」 63* 64* 「猫でもわかるプログラミング」( http://www.kumei.ne.jp/c_lang/ ) 65* ->「C言語編 第1部」 66* ->「コンソールアプリで文字色を変える」 67* 68**************************************************************/ 69 70bool CConsole::SetColor( COLORREF foreColor, COLORREF bgColor ){ 71 DWORD wAtt; 72 bool flag = false; 73 int counter = 0; 74 75 if( foreColor == PINK ){ 76 flag = false; 77 }else if( foreColor == BLACK ){ 78 wAtt = 0; 79 }else if( foreColor == AQUA ){ 80 flag = false; 81 }else if( foreColor == WHITE ){ 82 wAtt = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; 83 flag = true; 84 }else if( foreColor == RED ){ 85 wAtt = FOREGROUND_RED | FOREGROUND_INTENSITY; 86 flag = true; 87 }else if( foreColor == BLUE ){ 88 wAtt = FOREGROUND_BLUE | FOREGROUND_INTENSITY; 89 flag = true; 90 }else if( foreColor == YELLOW ){ 91 wAtt = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; 92 flag = true; 93 }else if( foreColor == GREEN ){ 94 wAtt = FOREGROUND_GREEN | FOREGROUND_INTENSITY; 95 flag = true; 96 }else if( foreColor == GRAY ){ 97 flag = false; 98 }else if( foreColor == -1 ){ 99 flag = false; 100 } 101 102 // 文字色設定 103 if( flag ){ SetConsoleTextAttribute( hOutput, wAtt ); counter++; } 104 105 // 背景色 も同様にする ( 文字数制限のため、省略 ) 106 107 // 背景色 or 文字色 が変更したなら true を返し、変更なしなら false を返す 108 if( counter == 0 ){ 109 return false; 110 }else{ 111 return true; 112 } 113} 114 115 116 117/************************************************************** 118* 119* 参 ・ 引: 120* 「EternalWindows」( http://eternalwindows.jp/ ) 121* ->「Windows開発」 122* ->「コンソール」 123* ->「コンソールへの入出力」 124* 125**************************************************************/ 126 127bool CConsole::GetColor( COLORREF &foreColor, COLORREF &bgColor ){ 128 // 未チェック 129return false; 130} 131 132 133 134/************************************************************** 135* 136* 参 ・ 引: 137* 「EternalWindows」( http://eternalwindows.jp/ ) 138* ->「Windows開発」 139* ->「コンソール」 140* ->「コンソールへの入出力」 141* 142**************************************************************/ 143 144bool CConsole::Write( const string str ){ 145 DWORD dwWrittenByte; 146 147 if( WriteConsole( hOutput, str.c_str(), str.size(), &dwWrittenByte, NULL ) != 0 ){ 148 return true; 149 }else{ 150 return false; 151 } 152} 153 154 155 156/************************************************************** 157* 158* 参 ・ 引: 159* 「EternalWindows」( http://eternalwindows.jp/ ) 160* ->「Windows開発」 161* ->「コンソール」 162* ->「コンソールへの入出力」 163* 164**************************************************************/ 165 166bool CConsole::Read( std::string &result ){ 167 DWORD dwReadByte; 168 char temp[1000]; 169 170 if( ReadConsole( hInput, temp, 1000, &dwReadByte, NULL ) != 0 ){ 171 return true; 172 }else{ 173 return false; 174 } 175} 176 177 178 179/************************************************************** 180* 181* 備 考: 182* Readメンバ関数を流用しただけ。 183* 184* 185**************************************************************/ 186 187void CConsole::Wait( void ){ 188 string temp; 189 190 this->Read( temp ); 191return; 192} 193

という感じです。

SetColorメンバ関数 の引数は RGBを#defineしたものです。( const でもいいですが。とにかく定数化したものです。 )

これを GUIタイプ ( WinMain関数でやる方 ) の WM_CREATE メッセージのところでインスタンス化し、表示。

WM_DESTROYメッセージで オブジェクト破棄する。

としました。

$rem 実行ファイル名が main.exe だとして。 $main

としても コンソール部分が 別ウィンドウ状態で開きます。

できれば CUIのときみたいに 呼び出し側の コンソールウィンドウ? に出力して、

$rem 実行ファイル名が main.exe だとして。 $main > help.txt

とすれば ファイルに書き込む...みたいにできたらいいのですが...

また、オブジェクトを複数作ってみたのですが、最初の一個は成功するのですが、

二個目は例外が投げられます。

複数の コンソールウィンドウを持つことは不可能なのでしょうか?

もしできるなら、たとえば

メインウィンドウ( GUI ) では 画像を表示等,
サブウィンドウ1 ( CUI ) では ファイル名を変更・編集・次の画像へ...みたいなコマンド入出力,
サブウィンドウ2 ( CUI ) では サブウィンドウ1 での結果を表示

みたいに出来そうなのですが...

CreateWindow関数で毎回作るのもいいですが 面倒だったりします。
クラス化, 関数化してもなぜかうまくいかない ( ウィンドウハンドルが無効等? ) でややこしい...

聞きたいことは、

■ GUIソフトに オプション "-h" または "/h" が与えられたときに 呼び出し側のコマンドラインに 直接出力する( ">" や ">>" を使えば ファイルに書き込まれる ) 方法

■ メインウィンドウは GUIタイプだが、サブウィンドウ(複数) は コンソールタイプとして複数のコンソールを持つ方法

です。

[情報]
言語 : C/C++
コンパイラ: MinGW
Window API: あり
OS : Windows 7

宜しくお願い致します。

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

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

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

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

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

coco_bauer

2017/05/20 04:17

「GUIソフト」の定義は何ですか? 「GUIタイプ」という聞きなれない用語の定義も教えてください。 これらが理解できないと質問の意味がわかりませんから。
BeatStar

2017/05/20 04:39

すみません。 どういう風に表現したらいいかわかりませんが、EDITとかのコントロールを使う方のGraphical User Interface です。
退会済みユーザー

退会済みユーザー

2017/05/20 14:09

この場合のGUI、CONSOLEの定義はEXEファイルのsubsystemがGUI(コンパイル時に-mwindowsオプションを付与)で呼び出されるエントリポイントがWinMainで、ダブルクリックで開いてもコマンドプロンプトが開かない状態のアプリケーションって事ですよね。
guest

回答5

0

ベストアンサー

■ GUIソフトに オプション "-h" または "/h" が与えられたときに 呼び出し側のコマンドラインに 直接出力する( ">" や ">>" を使えば ファイルに書き込まれる ) 方法

リンクオプションで、サブシステムをコンソールに設定すれば、コンソールアプリ同様にコマンドプロンプトにprintfできます。当然パイプやリダイレクトも使えます。
MinGWはほとんど使ったことがないのでそちらでのやり方は判りませんが、VC++なら構成プロパティのリンカー→システム→サブシステムをコンソール (/SUBSYSTEM:CONSOLE)に設定すれば、GUIアプリとして作ったプロジェクトもコンソールアプリになります。
ただし、そのままだとmain関数がないというリンクエラーになるので、以下のようなコードを追加すれば、コーンソールアプリ兼GUIアプリになります。

C++

1int main() 2{ 3 // 文字コードがUnicodeなら上、そうでなければ下 4 return wWinMain(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), SW_SHOW); 5 //return WinMain(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), SW_SHOW); 6}

■ メインウィンドウは GUIタイプだが、サブウィンドウ(複数) は コンソールタイプとして複数のコンソールを持つ方法

できません。コンソールウィンドウは一つのプロセスで一つしか作れないという仕様になっています。


追記

どうやらコマンドプロンプトとMinGWのシェルでは、GUIアプリ(サブシステムがWINDOWS)を実行する際の挙動が違うようです。MinGWのシェルはコマンドラインツールしか使ったことがなかったので、今まで知りませんでした。

コマンドプロンプトでは、GUIアプリには標準入出力用のハンドルが渡されません。そのため、パイプやリダイレクトも無視されます。実行すると終了を待たずにすぐにプロンプトに戻るのでそういう仕様にしているのだと思います。

一方MinGWのシェルでは、GUIアプリにも標準入出力用のハンドルが渡されます。実行すると終了するまで制御が戻らないのは、おそらくそのためでしょう。GUIアプリでprintfしてみたら、普通に出力されていましたし、リダイレクトも有効でした。
(ご質問時にうまくできなかったのはコマンドプロンプトで実行したからでしょうか?)

ということを踏まえて、

■ CUI + GUI
■ GUI のみ
■ CUI のみ ( 今回はスルー )

このように、状況に応じてコンソールウィンドウを開いたり開かなかったり、といったことをするには、とりあえず、MinGWのシェルを使うことが前提であれば、特に難しいことを考えることなくGUIとGUI+CUIの切り替えが実現可能だと思います。エクスプローラーで起動したときにコンソールウィンドウを表示したいという場合は、AllocConsole関数を使う方法で良いかと思います。

一方コマンドプロンプトで同じようなことをさせるのはかなり難しいと思います。コンソールアプリにすると、エクスプローラーで起動したときに必ずコンソールウィンドウが開きますし、GUIアプリではコンソールへの出力はできてもリダイレクトはできません。
GUIアプリに加えてコマンドプロンプトからの起動用のCUIアプリも作る(そしてパイプで受け渡し)、といったことをする必要があるかもしれません。

投稿2017/05/20 07:46

編集2017/06/15 14:06
catsforepaw

総合スコア5938

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

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

Chironian

2017/05/20 08:49

おお、確かにできました。なるほどです。
BeatStar

2017/06/14 07:47

ご回答ありがとうございます。 最近できてなかったのでチェックしてみました。 一応 catsforepaw様のご回答にあるようにやってみました。 MinGWでは /SUBSYSTEM:CONSOLE に相当するようなオプションはないので、 GUI を生成するという意味? の -mwindows を外して main関数から WinMain関数を呼び出すことでうまくいきました。 しかし、そのプログラムを単体 ( ダブルクリック ) で起動すると パパッと コンソールが一時的に開きます。 これを ■ CUI + GUI ■ GUI のみ ■ CUI のみ ( 今回はスルー ) と プログラムが決定できるようにしたいのですが、無理でしょうか? ( 最初の質問からすると アレかもしれませんが... ) 宜しくお願い致します。
catsforepaw

2017/06/15 14:00

いろいろ試してみて判りました。長くなるので回答の方に追記します。
guest

0

Subsystem:GUIのアプリケーションからAttachConsole(ATTACH_PARENT_PROCESS)でコンソールに書き込もうとすると、Subsystem:Consoleのアプリはコマンドプロンプトがアプリの終了まで待つのに対し、Subsystem:GUIのアプリは起動後終了を待たずに即次の入力を待つため挙動がおかしくなるそうです。
回避方法は起動時に/Start wait 実行ファイル名で起動するか、バッチファイルから起動するしか無い

で、GUIのアプリケーションなのにコマンドプロンプトにメッセージを表示できるのは下記のサイトを読んだ感じですと、

How do I write a program that can be run either as a console or a GUI application?

Subsystem:GUI(拡張子は.exe)の実行ファイルとSubsystem:Console(拡張子は.com)の同じ名前で違う拡張子の実行ファイルを用意して、

例えばVisual Studioの実行ファイルは、devenv.exeで、同じフォルダにdevenv.comがあるのですが、コマンドプロンプトからdevenv -hとすると実際にはdevenv.comの方が起動、子プロセスとしてdevenv.exeを呼び出しそこからコンソールに表示する内容を読み取ってるような感じですね。
なので、devenv.exeがスプラッシュスクリーンを一瞬表示した後にコマンドプロンプトにヘルプが表示される。

WinSCPと言うアプリもdevenvと同じような方法を取っていて

コマンドプロンプトからWinSCP.com -hで起動するとそのままコマンドプロンプトにヘルプを表示するのに対し、WinSCP.exe -hで起動するとAllocConsoleで新たにコンソールウィンドウを生成してそこにヘルプを表示するようになっていました。
こちらはオープンソースのアプリなのでソースを見ればなにか参考になるかもしれません。

ちなみにAdobeのブログにもWindow- and Console-Friendly Win32 Applicationsと言うタイトルで、subsystem:GUIのアプリからAttachConsole(ATTACH_PARENT_PROCESS)を使って居る場合はc:> start /wait dualmode.exeで起動するように書いてありました。

投稿2017/05/24 06:28

編集2017/05/24 06:29
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

0

Windowsアプリケーションでコマンドプロンプトから実行した時に標準出力を使ってメッセージを表示する方法はAttachConsoleで検索すると色々出てきます。

Windows API 実験室

>g++ main.cpp -mwindows EXEファイルのSubsystemにGUIがセットされエントリポイントがWinMain関数になり、コマンドプロンプトは開かず標準入出力も割り当てられない)

cpp

1#include <windows.h> 2#include <stdio.h> 3#include <iostream> 4 5int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR lpCmdLine, int) 6{ 7 FILE *fp = nullptr; 8 9 if (!AttachConsole(ATTACH_PARENT_PROCESS)) { 10 // コマンドプロンプトから起動していない場合は新たに開く 11 AllocConsole(); 12 } 13 14 // 標準出力の割り当て 15 freopen_s(&fp, "CONOUT$", "w", stdout); 16 17 printf("Hello, Console!\n"); 18 std::cout << "Hello, Console!" << std::endl; // coutもOK 19 fclose(fp); 20 21 // 標準出力の割り当てを解除 22 FreeConsole(); 23 24 return 0; 25}

※VC++の場合、freopen_sでは無くfreopenを使わないとFreeConsoleで例外が発生して停止します。


色々と調べた結果の修正版

cpp

1#include <windows.h> 2#include <tchar.h> 3#include <io.h> 4#include <fcntl.h> 5#include <stdio.h> 6 7int APIENTRY _tWinMain(HINSTANCE, HINSTANCE, LPTSTR lpCmdLine, int) 8{ 9 if (!AttachConsole(ATTACH_PARENT_PROCESS)) { 10 AllocConsole(); 11 } 12 13 HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE); 14 int fd = _open_osfhandle((intptr_t)hConOut, _O_TEXT ); 15 *stdout = *_fdopen(fd, "w"); 16 setvbuf(stdout, nullptr, _IONBF, 0); 17 18 printf("Hello, World!\n"); 19 20 Sleep(2000); 21 22 _close(fd); 23 FreeConsole(); 24 25 return 0; 26}

実行結果:

>g++ main.cpp -mwindows -o main.exe => subsystemをGUIにしてビルド >main.exe => ファイルにリダイレクトせずに実行 Hello, World! => コマンドプロンプトに表示される >main.exe > hello.txt => hello.txtにリダイレクト => コマンドプロンプトには表示されない >type hello.txt => hello.txtの中身をコマンドプロンプトに表示 Hello, World!

freopen_sを使用した最初の方法ではファイルにリダイレクトしても何も書き込まれませんでしたが、今回のはちゃんと書き込まれました。
これでいかがでしょう?

投稿2017/05/20 15:24

編集2017/05/21 05:33
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

catsforepaw

2017/05/21 01:57

残念ながら、その方法だとパイプやリダイレクトが使えないのです。いろいろ試したことはあったのですが、どうやらサブシステムがWindowsのときは、標準入力・出力・エラーのハンドルは渡してくれないようです。
catsforepaw

2017/06/15 14:10

私の回答の方にも書きましたが、コマンドプロンプトとMinGWのシェルとで挙動が違っていたようです。 MinGWのシェルではGUIアプリでもコンソールへの出力やリダイレクトは有効ですね。
guest

0

回答といえるほど確信ないですがWindows OS自体ではそのようなアプリケーションを作る能力はあると思います。

なぜならJavaなどで作ればそういうものを作れるからです。Java/C++のランタイムの詳細を知らないのでC++で簡単にできるかどうかは自分にはわかりません。ただスレッドは分ける必要があると思います。

一般的にGUIシステムはイベント指向で構築されておりWindowsも同様です。OS(あるいはGUIサブシステム)からのイベントを受け付けるメッセージポンプを監視しながら配送されてきたイベントに対するハンドラーを起動する専用のスレッドがあり、それでGUIを制御してます。C/C++/C#で作った場合でもそのようなモデルになっていますよね?一方、コンソールのような同期的な入出力前提のものはGUIスレッドとは独立した別のスレッドで行う必要があると思います。

C++/C#でコンソール入力とGUIを両立するプログラミングはしたことないのですが、Javaだと普通のCUIアプリケーションと同じ機能を通じてコンソール(というよりプロセスへ割り当てられている標準入力ストリーム)とmainスレッド上でやりとりしながらGUIは専用スレッド(Event Dispatch Thread)で並行して動作します。

C++での標準入力ストリームはstd::cinですが、C++のGUIアプリケーション用のランタイムがstd::cinと共存できるような仕組みに作られているなら、GUI,CUIのスレッドを分けてやればJava同様の使い勝手で動くような気もしました。

非常に安直な推測ですので「Win32 APIを用いたアプリケーションでそんな簡単にはできない」かも知れませんが。

投稿2017/05/20 05:46

編集2017/05/20 05:47
KSwordOfHaste

総合スコア18394

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

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

0

こんにちは。

本体は GUI なのに コマンドラインに表示可能...

やったことはないのですが、普通に標準出力へ出力したら出ないでしょうか?
標準入出力は親プロセスのものを引き継ぐので、普通にコンソールから起動したら その標準入出力を引き継ぐので、起動元のコンソールへ入出力できそうな気がします。

そのようなソフトをExplorerのような標準入出力が有効でなさそうなソフトから起動した時、どうなるのかちょっと興味深いです。入力は無限待ちかエラー、出力も破棄かエラーになりそうな気がします。

投稿2017/05/20 05:03

Chironian

総合スコア23272

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

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

BeatStar

2017/05/20 06:05

普通に cout, printfで出力するってことですよね? それなら昔試しました。 しかし、そこの部分だけ無視したような結果です。 ( 実際には動いていたと思いますが、出力先が定義されていないとかで実行不可。 )
Chironian

2017/05/20 08:50

本当ですね。やってみたらダメでした。catsforepawさんの回答が正解のようです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問