hide-from-taskbar-image

※ 注意
今回は web にあんまり関係ないプログラムの話が多めです
普段このブログ見てる人だとよくわからないことが多いかもです(私もわからないことばかりです)
また、プログラム専門な人からすると、「これは参考にできない良くないコードだね~」ってなりそうなものばかりです
読む場合はこのへんをご理解の上で読んで下さい


常駐ソフトがタスクバーにあると邪魔ですよね
できれば右下の△(タスクトレイ)にまとまってほしいです

自分でソフトを作るならタスクバーに表示するかどうかやタスクトレイに表示するかどうかを選べるみたいです
でも、すでにあるソフトとなるとちょっとむずかしいみたい
タスクトレイに持っていくのは無理そうですが、タスクバーから非表示にしてるソフトもあるのでできなくはないはず

できるの?

日本語のページを調べてると無理です、って書いてるページがいくつかありました
知恵袋とかだからあんまり信用できません
現にできてるソフトあるんだから

今度は英語のプログラム関係のところを調べてみると、ウィンドウを表示しなければ消せるようなことが書かれていました
言われてみたらできてるソフトは仮想デスクトップを作るソフトで、ウィンドウを表示していないのをタスクバーにも表示しないものです
最小化とは違ってタスクバーにも表示しない完全に非表示と言う機能みたい?


今回は付箋みたいな常駐で表示したままのソフトをタスクバーから消す方法を探してるのでコレだとだめです


もう少し探してみるとウィンドウの種類をツールウィンドウにするとタスクバーから消せるというのがありました
さっそく変えてみたかったのですがフリーソフトでウィンドウの種類をいじれるのは見当たりませんでした

見つけたところには Win32 API を使ったプログラムがあってすごく短いコードだったので勢いでやってみることにしました
これが地獄の始まりだとも知らずに・・・・

C++

C++ という何がなんだかわからない多くの人に恐れられてる言語があります
その言語でこう書けばできるようです

HWND hWnd = (HWND)0xD1B9E; // ダミーの値。実際はWindowに設定されてるのを取得する
long style = GetWindowLong(hWnd, GWL_STYLE);

ShowWindow(hWnd, SW_HIDE);

style &= ~(WS_VISIBLE);
SetWindowLong(hWnd, GWL_STYLE, style);

style = GetWindowLong(hWnd, GWL_EXSTYLE);
style |= WS_EX_TOOLWINDOW;
style &= ~(WS_EX_APPWINDOW);
SetWindowLong(hWnd, GWL_EXSTYLE, style);

ShowWindow(hWnd, SW_SHOW);

これだけみるとなんかできそうな気がしました
HWND というのは Windows に詳しい人ならたまに耳にするかもしれないものです
ウィンドウのハンドルというものでプログラムからウィンドウを扱うときに使われます

その HWND から、今のスタイルを取得して変更して、それを設定します


HWND を調べるフリーソフトは色々あります
WinLister とか

試しにウィンドウのハンドルを調べて上のプログラムのダミーのところを書き換えて実行してみます


おおぉ、ホントに消えた!!!



無事非表示できました
意外と簡単じゃん~

・・・ここでやめておけば時間は無駄にならなかったのです・・・・・

GUI ほしいな

別のソフトでハンドル調べてソースコード書き換えて再実行なんてやってられないよね~
ということで画面に一覧が並んでクリックしたら非表示にできるようにしたいです


とは言ってみても C++ で Windows アプリをつくるのはとっってもつらいです
昔、 JavaScript がわけわからないころによく使うテキスト変換をワンクリックでできるようにしたくて C++ CLI というので Windows アプリを作ったことがあります
すごく大変でもう使いたくないと思った嫌な記憶です

あとは ゲーム用の DirectX を簡単に使えるようにしたライブラリでフリーセルみたいなトランプゲームを作ったことがあります
これも C++ だったはずです
使い方のサンプルが多かったからか C++ CLI より簡単に作れたのですが、そもそもゲーム用ですし全体的に重かったのでこれもNGとします


やっぱり画面作るのは HTML と CSS がいいよー、と思って出てきた選択肢はこちら

▶ Chrome 拡張機能にしよう
▶ Electron でも Win32API 使えたりしない?
▶ CEF とかどうよ

1つめ: Chrome 拡張機能

一番慣れてる方法です

・・・・が!、 Win32API を使えなければいけません
もちろん Chrome 拡張機能自体にそんな機能はありません

いまのところ、Chrome 拡張機能でできないことをするためには、 NativeMessaging と NativeClient の2つがあります

NativeMessaging というのは JavaScript の postMessage でメッセージ受け渡しするような感じで、ネイティブなアプリ (exe) とやりとりできるものです
比較的簡単そうです
もちろん exe を作る知識が必要なので、私みたいな JavaScript でウェブいじってる人程度の人にはすごくハードルが高いところです


NativeClient は色々変わってきていて 少し前には プロセッサ(CPU)に依存しない PNaCl になったと思ったら、需要ないから 2018 年にはサポートやめて WebAssembly に移行するとか書いてました
専用の SDK も Emscripten というツールになるようです

asm.js や WebAssembly で Unity をウェブで動かせる、とか聞いてどんなのか気になったので、1, 2年前に Emscripten をちょっと触ったことがありますがいまいちよくわからなかったです
とりあえず速度はすごかったというのだけ記憶に残ってます

前にフラッシュが動かなくなるとかいわれていたのも NPAPI という昔の技術を廃止して PPAPI という新しいのだけになるからで、これらの API が NativeClient で使われています

とりあえずこっちは複雑すぎるし、 C++ などで書くことが多く大変なので使うなら NativeMessaging です

2つめ: Electron

JavaScript でウィンドウズアプリが作れるものです
ほんのちょっと触れてみて Chrome 拡張機能でいいやと投げました

中身は Node.js と Chrome なので Win32 API を Node.js で使えるか次第です
調べてみると一応ライブラリはあるけど、メジャーじゃなくてちょっと心配な感じ

一番良さそうなのが .NET でプログラムを書いてそれを Node.js から呼び出すものみたい


難しそうだし、非表示の切り替えする部分も .NET に置き換えないとダメなので、これは積極的に選びたくないかな

3つめ: CEF

これも Chrome です
C++ や .NET や Java など色々なプログラミング言語から Chrome を操作できるライブラリです
JavaScript で iframe 内の window を操作する感じに近いとか近くないとか

実はこれもほんのちょっと使ったことがあります
URL のページを開く、だけなら簡単でした
でも、Chrome 拡張機能みたいにあれこれ操作するのは情報が少なくてよくわからなかったです

Chrome 拡張機能、君に決めた!

一番楽にできそうなのが Chrome 拡張機能なのでこれにします!
Windows のタスクバー操作をなぜか Chrome で操作するって変な感じしますが気にしては負けです

NativeMessaging だけが心配ですがなんとかなるでしょう
この選択がのちに(ry

C++なんて大っ嫌い

NativeMessaging は標準入力と標準出力で通信します
C++ ですることは、

○ 入力を受け取って何をするか判断する(一覧を返したり非表示にしたり)
○ 一覧命令が来たらウィンドウ一覧の情報を送り返す
○ 非表示命令が来たら非表示にする


たったこれだけなんですが

日本語周りですごく苦労することになりました

まず、ウィンドウ一覧の表示がサンプルコードコピペじゃ動きません

とりあえず表示してみてもウィンドウタイトルは違うはずなのに全部同じ数字の並びだったり・・・
今思えば文字コードじゃなくてアドレスだったのかな?

Win32API では string 型じゃなく wchar_t というのが使われてるようで、型が違います
とりあえず char じゃなくてコンパイル時に自動でやってくれる TCHAR にすると文字は出ました
でも日本語が出ないです

表示方法も wcout と _tprintf で結果が違います
locale を日本にすると直るという記事を見てやってみると、表示されたけど逆に動かなくなるところが出てきたり・・・

VisualStudio のバージョンによってはそんな問題もあるみたいだけど、めんどくさすぎる!!

リテラルも _T とか _TEXT とか TEXT とかマクロ使わないとダメで何が何やらって感じです


それと、なぜか Atom (テキストエディタ)のタイトルを表示するとそれ以降が全く表示されなくなりました
特殊な文字が含まれていてそれ以降がおかしくなるようです

C# のサンプルもあったのでそっちをコピペして動かしてみるとちゃんと表示できてました
他にフリーソフトで確認してもちゃんと見えています

もしかしてソースファイルのエンコーディングとか関係あるのかな?とSJISだったのをUTF-8にしてみました
逆に動かなくなりましたが、それは BOM付き UTF-8 にすると解決できました
Atom があると表示できないのは相変わらずです

「#pragma execution_character_set("utf-8")」という命令を書いておけばいいらしいと聞いて書いてみましたが何も変わってません

結局 Atom を閉じるとそれ以降が表示されましたが、タイトルによっては動かないなんて欠陥品ですよね・・・
しかも表示できると書いてきましたが、それはファイルに出力した時で、標準出力に表示するとやっぱりちゃんと出ません

一度ファイルに出力してファイルを読み込めば大丈夫でしたが、こんなことのためにファイル出力は避けたいです
そもそも同じ文字なのに TCHAR と string で変換できないとか不便すぎ


丸一日使ってこれなので、出来る気がしないのでタイトルとかはやめてハンドルだけの一覧にしました
結局一覧が出てもどれが対応してるのかわからなくて実用性×なクオリティ

付箋アプリの HWND はたぶんこれだな! カチッ

Chrome がきえたあああああああああああ!!!

な未来が見える・・・

hide-from-taskbar-cpp


それにウィンドウ一覧を取得だけじゃなくて NativeMessaging の入出力も大変でした

最初に 4 バイトで本体のサイズを送信(受信)して、 4 バイトで示されたサイズ分読み取るのですが、 C++ でそんな特殊な読み取り方がわかりません
ググってみても人によって書き方が違います

例のごとくコピペしても動いたり動かなかったり・・・・


結局紹介されてるコードを混ぜて魔改造したこれで通信はできてました

string receive() {
string str;
unsigned int l;
cin.read(reinterpret_cast<char*>(&l), sizeof(l));
for (unsigned int i = 0; i < l; i++) str += getchar();
return str.substr(1, str.size() - 2);
}

void send(wstring str) {
str = replace(str, _T("\\"), _T("\\\\"));
str = replace(str, _T("\""), _T("\\\""));
str = replace(str, _T("\n"), _T("\\n"));
str = _T("\"") + str + _T("\"");
unsigned int l = str.size();
cout.write(reinterpret_cast<char *>(&l), sizeof(l));
wcout << str << flush;
}

void send(string str) {
str = replace(str, "\\", "\\\\");
str = replace(str, "\"", "\\\"");
str = replace(str, "\n", "\\n");
str = "\"" + str + "\"";
unsigned int l = str.size();
cout.write(reinterpret_cast<char *>(&l), sizeof(l));
cout << str << flush;
}

ただ拡張機能のページをリロードすると受信を無限に続けて CPU 使用率が 1 コア分 MAX になってしまう問題がありました
receive 関数を呼び出すループはサンプル通りなので何が原因かわかりません

リロードしなければ大丈夫ですがリロードしてしまうと暴走するなんて怖くて使えません
プロセスが残るのがダメみたいなので、 onbeforeunload で終了命令を送るようにしてとりあえずの対処になってます

あとは JavaScript にあるような便利関数がデフォルトじゃないので自作する必要があったのも面倒でした


やっぱり C++ にはもう関わりたくないです
でもゲームってこんなので作ってるだよねー
すごいなー

私なんて1日でこれなので、1週間も使ったらストレスで爆発しそうです

後日談: F# でやってみた

C++ でダメだったところが文字列なので、最近の言語だとそのあたりは統一されてそう
C++ がつらすぎて C++ の代替のための言語が色々できてますしね

NativeMessaging するなら標準入出力が使えればいいので、ブラウザ JavaScript みたいな特殊なもの以外はたいていなんでもできるはず
あとは Win32API の対応状況です

簡単に調べてみると

○ D:Win32API は簡単に使えるようになってるみたいだけどわかりやすいサンプルが少なめ
 wchar や TCHAR と言う単語が見えたので似たことが起きそう

○ go: できるみたいだけどできないのもあるような書かれ方
 D より情報少なそう

○ rust: go よりも情報なくて外部のライブラリだより?

○ Java 系: 直接はムリで特殊なもの経由してるみたい?
 Scala では直接実行してそうなサンプルあったけど TCHAR 使ってた

○ .NET 系: マイクロソフトが作ってるだけあってすごく簡単に書けてました
 TCHAR とか使わなくていいところも嬉しいです

○ Python: すごく簡単にかけていました
 動的言語バンザイ!


Python か .NET系 が簡単そう

どれにしようかな


流れで言うなら C# を使うべき?
でも C++ の苦痛のせいで、 「C」 という言葉もみたくありません!!

ということで F# にしてみました

Python でもよかったけど、入出力とかバイト単位の処理とかって動的言語より静的言語のほうが簡単にできそうという偏見です

C++ なんていらなかったんだ

F# を選んでみたけど、使ったことないです
書き方も知らないです

それで何故選んだのかと言われると、ちょっと困ります
C# 除くと VB.NET と F# だったので VB ってエクセルのよくわからないやつってイメージあるので・・・
あと .NET 言語の中では一番新しいみたいですしね


そんな感じで選んだので、そもそも変数と関数はどう書くんだ?ってところから調べていて C++ 以上に調べる時間必要だったはずなのに半日程度で作れてしまいました

VisualStudio がないとつらそうな予想外のコンパイルエラーが色々ありましたが、そこまでつまることはありませんでした

できた画面はこれです

hide-from-taskbar-list


C++ で妥協したのとは違います
ちゃんとタイトルやクラス名がでていて、外部ソフトで HWND 調べること無く使えます!

C++ 版は↓ですからね・・・

hide-from-taskbar-cpp


ところで、ツールウィンドウになると、タイトルバーがシンプルになります
アイコンや最小化がなくなります
最小化ができないのでタスクバーになくても安心です

サクラエディタだとこうなりました

sakura-toolwindow

付箋などの独自の見た目をしているウィンドウは特に変化がありませんでした


作ってる最中にミスで、スタイルを変更せずに取得したのをそのまま設定するとなぜか並びが逆順になりました
もともとのスタイルがそのままなので変換しなそうですけど、なんで???

revwindow


最初の目的は達成して満足です

あとはタイトルが空文字のウィンドウもあるのでできればアイコンがあると便利かな
C++ のときに C++ でやるのは絶対無理そうということで外していましたが、 .NET だとアイコンのハンドルから簡単に画像ファイルを作れるようです
Base64 の変換も .NET framework の標準機能にあります

また NativeMessaging 通信は JSON 形式の受け渡しで、直接文字列を送っても JSON 形式の文字列ということで 「"」 で囲まれます
それなら JSON のオブジェクトで扱うほうが良さそうですが C++ ではこれまた大変そうなので全部文字列型にしていました
.NET だと JSON の扱いも簡単になるので JSON 形式にして、自分でデータの分割処理を書かなくて済むようにもできます

NativeMessaging の使い方

最後になりましたが、 Chrome 拡張機能の作り方ということで、 NativeMessaging の使い方を紹介します

拡張機能側

拡張機能の方は基本は普段通りに書きます
manifest.json の permissions に "nativeMessaging" を追加しましょう

{
"manifest_version": 2,
"name": "messaging-extension",
"version": "1",
"options_page": "app.html",
"permissions": [
"nativeMessaging"
]
}

JavaScript で chrome.runtime.connectNative を使って通信を開始します
app.html を作ってそこで読み込む app.js をこうしました

var port = chrome.runtime.connectNative("messaginghost")
port.onMessage.addListener(msg => {
console.log(msg)
})
port.onDisconnect.addListener(() => {
console.log("Disconnected")
})
port.postMessage("hello")

messaginghost のところは自分でつける名前です
window.postMessage みたいな感じで通信できます

NativeMessaging ホスト側

使うパソコンのレジストリに NativeMessaging のマニフェストファイルの場所を登録します

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\messaginghost]
@="C:\\ChromeExtension\\messaginghost\\nmh-manifest.json"

↑を書き換えて .reg ファイルに保存して実行するのが簡単です
NativeMessaging のホストの方では messaginghost と言う名前にしてます
拡張機能と一緒にする必要はないです

レジストリには 「C:\\ChromeExtension\\messaginghost\\nmh-manifest.json」 と登録したので、ここに NativeMessaging 用のマニフェストファイルを置きます

{
"name": "messaginghost",
"description": "My Application",
"path": "C:\\ChromeExtension\\messaginghost\\app.exe",
"type": "stdio",
"allowed_origins": [
"chrome-extension://coooemhlhoaamokajbkhlcggpidjbngc/"
]
}

exe ファイルの場所と拡張機能の ID を書きます

NativeMessaging は、ひとつの拡張機能にひとつのネイティブアプリが対応するものではありません
ひとつの拡張機能から複数のネイティブアプリに接続できますし、ひとつのネイティブアプリが複数の拡張機能から接続されることもできます

例えばコマンドプロンプトでコマンドを実行して結果を返すネイティブアプリを作っておけば、複数の拡張機能でひとつのネイティブアプリに実行したいコマンドを送信するだけで済みます


これで JavaScript から 「chrome.runtime.connectNative("messaginghost")」 が実行されると app.exe が実行されます

exe を作るところ以外はこの通り簡単です

使ってみたい人

C++ 版は隠滅しましたが F# 版は残ってるので使ってみたい人は下からどうぞ
ネイティブアプリな上になれない(初めて使う)言語で作ってるのでいつも以上に自己責任を強調しておきます


exe ファイルのサイズが大きくてブログの容量的につらいのでそれぞれのソースを置いてるので自分で組み立ててください
(やることは、コンパイルして exe を作って、ファイルパスを修正して、レジストリ登録と拡張機能のインストールです)

Gist

まとめ

プログラム関係を「簡単にできそうじゃん」でやってみるのは危険
貴重なゲームする時間を2日ほど失いました・・・