LoginSignup
3
2

More than 5 years have passed since last update.

Wallpapper Engineみたいなのを作りたい その2

Posted at

前回の続きです
前回を見てない方はこちらからどうぞ
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.execonhost.exeは別物ですよ
今度はコマンドプロンプトconhost.exeを起動してみてください
新しいウィンドウが立ち上がりましたね?
通常コマンドプロンプト上でcmd.exeを起動した場合新しいウィンドウは立ち上がらずそのコンソールの中で新しいコマンドプロンプトが起動しますよね
2019-03-20_014042.png
コマンドプロンプトのプロセスですWindows コマンド プロセッサコンソールウィンドウホストが起動していますね
2019-03-20_014218.png
cmd.exeを2つコマンドプロンプトから起動しました
Windows コマンド プロセッサが3つ起動していますね。
ですが、コンソールウィンドウホストは1つだけです。
では次PowerShellを起動してみましょう
2019-03-20_013044.png
Windows PowerShellコンソール ウインドウ ホストが起動していますね
では、次、コマンドプロンプトからPowerShellを起動してみましょう
2019-03-20_014423.png
はい、Windows Powershell Windows コマンド プロセッサ コンソール ウィンドウ ホスト
の3つが起動しています。
多分もうお判りでしょう
よくコマンドプロンプトのことをコンソールという方が居ますが、間違っています
いや完全に間違っているとは言い切れないんですが、、
コンソールはあの文字がだーってあるウィンドウのことを指します
そのコンソールを管理しているのがconhost(コンソール ウィンドウ ホスト)です
conhostを単体で起動した場合自動的に標準のコマンドプロセッサも立ち上がるので、conhost.exeを起動したときもコマンドプロンプトが立ち上がりました。
コマンドプロンプトとはそのコンソールと、内部のコマンドプロセッサのこと、または内部のコマンドプロセッサのことを指します。
cmd.exeや、powershellを起動したときに一緒にconhostを起動し、関連付けられます。
コマンドプロンプトまたはpowershellから起動した場合既に親のconhostは存在するのでそのconhostに関連付けられます
ちなみにそのconhostコマンドプロンプトpowershellの中継としてcondrvというドライバが存在します
2019-03-20_020610.png
コンソールアプリケーションの標準出力($CONOUT $CONIN)などもこのドライバを経由してコンソールに表示されます。

で今回起動するのはconhostの方です
理由はあとで説明します。
とりあえずプロジェクト作成
VisualC++ の空のプロジェクトを作成
CustomWallpaper-github
でいろいろ初期設定を
pch.hと一応cph.cppを作成

pch.h
#pragma once

#include <iostream>
#include <Windows.h>

プリコンパイル済みヘッダーですね
作成後、プロジェクトのプロパティからc/c++ > プリコンパイル済みヘッダー
プリコンパイル済みヘッダー作成 (/Yc)
プリコンパイル済みヘッダーファイルpch.hに設定
後はmain.hmain.cppを作成
エントリーポイントはWinMainを仕様します。

application.h
#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;
    }
};
application.cpp
#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
あとAttachConsoleAllocConsoleで関連付けたコンソールもFreeConsoleで解放してください。
FreeConsole
書いたコードはこんな感じ
今回追記した部分だけです

application.h
class application final {
    FILE *out = nullptr, *err = nullptr, *in = nullptr;
}
application.cpp
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

application.cpp
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_lineconhost.execmd.exeに変えてみましょう。
2019-03-21_012845.png
新しいコンソールは起動せず、既に存在するコンソールの上でコマンドプロンプトが起動してしまいます。
powershell.exeとした場合も同様です。
なので新しいコンソールを立ち上げるためにconhost.exeを起動しました。
今まpowershellの使用を推奨されているのでpowershellを起動するようにしておきましょう
cmd_lineの文字列をconhost.exe powershellとすればpowershellが起動します。

ということで壁紙に貼り付けるアプリケーションの起動は完了しました。
次回はこの起動したpowershellを壁紙として貼り付けるコードを考えてみます。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2