前回の続きです
前回を見てない方はこちらからどうぞ
Wallpaper Engineみたいなのを作りたい その1
取りあえず何も無しからいきなり作り始めるのは無理なので今分かってることから仕様をある程度決めていきましょう
#仕様
- 開発言語はVisualC++
- 第一の機能は壁紙としてコンソール(conhost.exe)を表示させる
- 勿論操作もできるように
プロジェクト名はCustom Wallpaperとでも
#環境
- Windows 10 Home Edition 1803
- CPU i7-6700k
- GPU GTX 750Ti
- RAM 16GB 2133MHz
- Visual Studio 2017
#conhost.exeを起動する
conhost.exe
とはまぁ簡単に言えばコマンドプロンプト
やPowerShell
などの入出力デバイスです
といってもわかりませんよね
試しにWin + R
を押してconhost.exe
と入力し実行してみてください。
きっとコマンドプロンプト
が起動するはずです。
ですがcmd.exe
とconhost.exe
は別物ですよ
今度はコマンドプロンプト
でconhost.exe
を起動してみてください
新しいウィンドウが立ち上がりましたね?
通常コマンドプロンプト
上でcmd.exe
を起動した場合新しいウィンドウは立ち上がらずそのコンソール
の中で新しいコマンドプロンプト
が起動しますよね
コマンドプロンプト
のプロセスですWindows コマンド プロセッサ
とコンソールウィンドウホスト
が起動していますね
cmd.exe
を2つコマンドプロンプト
から起動しました
Windows コマンド プロセッサ
が3つ起動していますね。
ですが、コンソールウィンドウホスト
は1つだけです。
では次PowerShell
を起動してみましょう
Windows PowerShell
とコンソール ウインドウ ホスト
が起動していますね
では、次、コマンドプロンプト
からPowerShell
を起動してみましょう
はい、Windows Powershell
Windows コマンド プロセッサ
コンソール ウィンドウ ホスト
の3つが起動しています。
多分もうお判りでしょう
よくコマンドプロンプト
のことをコンソール
という方が居ますが、間違っています
いや完全に間違っているとは言い切れないんですが、、
コンソールはあの文字がだーってある__ウィンドウ__のことを指します
そのコンソール
を管理しているのがconhost
(コンソール ウィンドウ ホスト
)です
conhost
を単体で起動した場合自動的に標準のコマンドプロセッサ
も立ち上がるので、conhost.exe
を起動したときもコマンドプロンプトが立ち上がりました。
でコマンドプロンプト
とはそのコンソール
と、内部のコマンドプロセッサ
のこと、または内部のコマンドプロセッサ
のことを指します。
cmd.exe
や、powershell
を起動したときに一緒にconhost
を起動し、関連付けられます。
コマンドプロンプト
またはpowershell
から起動した場合既に親のconhost
は存在するのでそのconhost
に関連付けられます
ちなみにそのconhost
とコマンドプロンプト
やpowershell
の中継としてcondrv
というドライバが存在します
コンソールアプリケーションの標準出力($CONOUT
$CONIN
)などもこのドライバを経由してコンソール
に表示されます。
で今回起動するのはconhost
の方です
理由はあとで説明します。
とりあえずプロジェクト作成
VisualC++ の空のプロジェクトを作成
CustomWallpaper-github
でいろいろ初期設定を
pch.h
と一応cph.cpp
を作成
#pragma once
#include <iostream>
#include <Windows.h>
プリコンパイル済みヘッダーですね
作成後、プロジェクトのプロパティからc/c++ > プリコンパイル済みヘッダー
プリコンパイル済みヘッダー
を作成 (/Yc)
プリコンパイル済みヘッダーファイル
をpch.h
に設定
後はmain.h
とmain.cpp
を作成
エントリーポイントはWinMain
を仕様します。
#pragma once
class application final {
static application* _instance;
int _exit_code = 0;
HINSTANCE _app_instance;
public :
static application* instance() {
return _instance;
}
explicit application(HINSTANCE h_instance) : _app_instance(h_instance) {
_exit_code = 0;
_instance = this;
}
void init();
void run();
void fin();
int exit_code() const {
return this->_exit_code;
}
HINSTANCE app_instance() const {
return this->_app_instance;
}
};
#include "pch.h"
#include "application.h"
application* application::_instance;
void application::init() {
}
void application::run() {
}
void application::fin() {
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/) {
application app(hInstance);
app.init();
app.run();
app.fin();
return app.exit_code();
}
まぁ説明は必要ないですね。
で、まずコンソールが無いとデバッグがめんどくさいです。
なのでアプリケーションに関連付けたコンソールを起動します。
AllocConsole()
関数を実行すると自身のアプリケーションと関連づいたコンソールが起動します
でも、コンソールからアプリケーションを起動した場合新しいコンソールが起動します。それは煩わしいですよね。
なのでAttachConsole
を利用し、それで失敗した場合にAllocConsole
でコンソールを確保します
AttachConsole
AllocConsole
ただしこのままでは標準入出力はこのコンソールではありません。なのでコンソールを標準入出力にします。
freopen_s
関数を使うとストリームを割り当てることができます。
freopen_s
のファイルパスをCONOUT$
にすると標準出力
CONIN$
のにすると標準入力です。
freopen_s
で開いたものはちゃんと最後にfclose
してあげてください。
freopen_s
fclose
あとAttachConsole
やAllocConsole
で関連付けたコンソールもFreeConsole
で解放してください。
FreeConsole
書いたコードはこんな感じ
今回追記した部分だけです
class application final {
FILE *out = nullptr, *err = nullptr, *in = nullptr;
}
void application::init() {
if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
AllocConsole();
}
freopen_s(&this->out, "CONOUT$", "w", stdout);
freopen_s(&this->err, "CONOUT$", "w", stderr);
freopen_s(&this->in, "CONIN$", "r", stdin);
std::cout << "initialize.\n";
}
void application::fin() const {
std::cout << "finalize.\n";
fclose(this->out);
fclose(this->err);
fclose(this->in);
FreeConsole();
}
さて、あとはrunメソッドにコードを書き込んでいくだけですが。
今回conhost
の起動はCreateProcess
関数を使います。
CreateProcess
void application::run() {
std::cout << "running.\n";
PROCESS_INFORMATION process_information = { 0 };
STARTUPINFO startup_info = { sizeof(STARTUPINFO) };
char cmd_line[] = "conhost.exe powershell.exe";
bool result = CreateProcess(nullptr, cmd_line, nullptr, nullptr, false,
NORMAL_PRIORITY_CLASS, nullptr, nullptr, &startup_info, &process_information);
if (!result) {
write_error_message(GetLastError());
system("pause");
this->_exit_code = GetLastError();
return;
}
this->ps_process_id = process_information.dwProcessId;
system("pause");
}
//application.hに static void write_error_message(DWORD error_code);を追加
void application::write_error_message(DWORD error_code) {
LPSTR lp_message_buffer = nullptr;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr,
error_code, LANG_USER_DEFAULT, reinterpret_cast<LPSTR>(&lp_message_buffer), 0, nullptr);
std::cerr << error_code << " : " << lp_message_buffer << "\n";
LocalFree(lp_message_buffer);
}
これで新しいコンソールが起動します。
で、なぜconhost
を起動したかを説明します
試しに cmd_line
のconhost.exe
をcmd.exe
に変えてみましょう。
新しいコンソールは起動せず、既に存在するコンソールの上でコマンドプロンプトが起動してしまいます。
powershell.exe
とした場合も同様です。
なので新しいコンソールを立ち上げるためにconhost.exe
を起動しました。
今まpowershell
の使用を推奨されているのでpowershell
を起動するようにしておきましょう
cmd_line
の文字列をconhost.exe powershell
とすればpowershell
が起動します。
ということで壁紙に貼り付けるアプリケーションの起動は完了しました。
次回はこの起動したpowershell
を壁紙として貼り付けるコードを考えてみます。