タスクバーからツイートしたい!

これは CAMPHOR- Advent Calendar 2020 の5日目の記事です

皆さんこんにちは @kmconner です! 今回は Windows のタスクバーにツイートボックスをつけてみた、という内容です。かなりマニアックな内容なのでどこまで需要があるのか分かりませんが、こんな世界もあるんだと興味を持ってもらえると嬉しいです!

きっかけ

Windows 10 になってからタスクバーに新しく検索ボックスが追加されました。自分はその検索ボックスは使わないので非表示にしてるのですが、こんな感じでツイートボックスを配置できたら自分の Quality of Twitter Life が上がるのでは?と思ったのがきっかけです。

調べてみた感じ、実装は少しややこしいものの Windows の基本的な開発技術を組み合わせることで実現できることが分かったので作ってみました。

作ったもの

Windows のタスクバー上にテキストボックスとツイートボタンを表示し、そこに文字を打ち込んでボタンを押すとツイートが送信されるようになっています。Ctrl-Enter でも送信できます。

含まれているソフトウェアとしてはツイートボックスを表示するためのプログラムが含まれた DLL 、そのDLL からのリクエストを受けてツイートを送信するプログラム、各種設定を行うための GUI アプリです。ツイートを送信するときには名前付きパイプを使ってツイート送信するプログラムと DLL との間で通信が行われます。

このアプリケーションは GitHub にてオープンソースで公開しているので気になった方は是非使ってみてください! URL は https://github.com/KMConner/TaskbarTweet です。

使用技術

タスクバー上に何かボタンやテキストボックスを表示する際には DeskBand と呼ばれるプログラムを書く必要があります。

DeskBand を実装し、タスクバー上にそれを表示させるには IDeskBand2 というインタフェースを実装して COM で公開する必要があります。これだけ聞いても何のことかわからないと思いますが、詳細はあとで説明します。

この記事ではこのプログラムの動きを理解するにあたって特に重要な技術である COM に関して簡単に解説しています。 本来であれば Win32API を使用した GUI アプリケーション開発に関しても記述すべきと考えられますが、それをしてしまうとあまりにもこの記事が長くなってしまうため、今回は省略しています。詳しく知りたい方は https://docs.microsoft.com/en-us/windows/win32/learnwin32/your-first-windows-program に詳しい解説があるので是非参考にしてください。

COM

COM とは Component Object Model の略で、DLL や EXE が相互に通信するための規格です。主に Windows 上で使用されている規格で、 OS の API 呼び出しなどもこれを使って行われることがあります。COM はサーバー・クライアントモデルを使い、機能を提供する側をサーバー、機能を呼び出す側をクライアントと呼びます。一般にサーバークライアントモデルと聞くと HTTP のようなプロトコルをイメージしがちですが、COMはそれとは随分と違ったものに見えるかもしれません。

COM を用いた処理の呼び出し

基本的には、通常のサーバークライアントモデルの通信と同じで COM もクライアント側がサーバー側の処理を呼び出すのが基本です。

COM における処理の呼び出し時の動作はサーバーとクライアントが同一プロセス内にある場合と別プロセスにある場合で少し変わってきます。今回開発したプログラムでは同一プロセス内にサーバー、クライアントが存在するため、そのケースに関して簡単に説明します。

このような、クライアントと同じプロセス内で動作するサーバーのことを In-process server と呼び、プログラムは DLL 内に格納されます。 (それに対して別プロセス内で動作するサーバーのことを out-of-process server と呼び、プログラムは EXE の中に格納されます。) COM クライアントが in-process server の処理を呼び出す際には、サーバーの DLL に含まれるプログラムをクライアントのプロセスのメモリ空間にロードし、関数を呼び出します。つまり、通常の関数呼び出しにかなり近い処理を行うことになります。

COM サーバーは CLSID (実態は GUID) というユニークな識別子を持ち、その情報がレジストリに登録されます。クライアントがサーバーの実装されたバイナリの場所を調べる際にはこれらの情報を使用します。

これ以上の詳細や、 Out-of-Process-Server の呼び出しに関する詳細は https://docs.microsoft.com/en-us/windows/win32/com/inter-object-communication あたりを参照してください。自分も out-of-process server との通信に関してはあまりちゃんと理解していません。

COM を使ってできること

COM は実際に様々な用途に使用されていますが、ここではそれらの一部を紹介します。

1つ目の例は Explorer の拡張です。 Explorer を何らかの形で拡張するようなプログラムを作成する際には COM を使用することが多く、特定の拡張子のファイルに対してサムネイルを表示する、特定のファイルに対してコンテキストメニューに項目を追加する、といったこともできます。Windows における標準的な設定では、タスクバーは Explorer によって表示されています。そのため、今回のようにタスクバー上にテキストボックスやボタンなどを表示する際にもこの方法で実装するのが一般的です。 このようなケースでは、先述の通りExplorer をクライアントとする in-process server を開発するのが一般的です。

2つ目はOffice系アプリの操作です。 1つ目の例とは異なり、このような場合には Out-of-Process Server を使用することが一般的です。以下のようなコードを使えば自動的に PowerPoint のスライドショーのページ切り替えをすることができます。

using System;
using System.Threading;
using Microsoft.Office.Interop.PowerPoint;

namespace ConsoleApp4
{
    class Program
    {
        static void Main(string[] args)
        {
            Application app = new Application();
            Presentation presentation = app.Presentations.Open2007(@"C:\Users\hogehoge\Desktop\test.pptx");
            SlideShowWindow slideShow = presentation.SlideShowSettings.Run();
            Thread.Sleep(5000);

            slideShow.View.First();

            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(1000);
                slideShow.View.Next();
                Console.WriteLine("Next Page...");
            }

            Thread.Sleep(1000);
            slideShow.View.Exit();
        }
    }
}

昔は同じような感じで iTunes の再生、停止、再生中のトラック情報の取得などができたのですがどうやら現在はできなくなってしまっているようです。また、多くの人が愛してやまない(?) 古のブラウザ Internet Explorer もページのナビゲーションなどを COM 経由で操作することがでけいます。

C++ による COM サーバーの実装

ここでは、Visual C++ を使用した COM サーバーの実装方法を紹介します。

COM の特徴として、呼び出す API が全てインタフェースとして実装されていることがあります。C++ の仕様にはインタフェースの機能はありませんが、以下のように定義された純粋仮想関数のみが含まれるクラスが代わりに使用されます。この記事でも、純粋仮想関数のみで構成されたクラスのことを「インタフェース」と呼ぶことにします。ちなみに今回は標準で定義されているインタフェースを使用しましたが、自分で独自のインタフェースを定義することもできるようです。

interface IUnknown {
  virtual HRESULT QueryInterface (REFIID riid, void **ppvObject) = 0;
  virtual ULONG   AddRef () = 0;
  virtual ULONG   Release () = 0;
};

API の公開にインタフェースを用いることで、モジュール間の結合が疎になるという利点があります。 本来であれば、ある EXE から 別の DLL に含まれる関数を呼び出す際には DLL をビルドした後に EXE をビルドする必要があります。今回作成した DLL は Explorer.exe から処理が呼び出されますが、この DLL をビルドしなおした際に Explorer を再度ビルドする必要はありません。(もちろんこんなことは一般ユーザーにはできませんが。) これも、 API の公開にインタフェースを用いることの利点です。

今回開発したプログラムにおける処理

今回開発したプログラムにおいてはクライアントからの要求に応じてタスクバー上に表示するウィンドウを作成し、そのハンドルを返す機能や、そのウィンドウの表示、非表示を切り替える機能などが含まれています。 Windows においてはファイル、ウィンドウ、スレッドなどのシステムリソースをオブジェクトとして管理しています。ハンドルとは、それにアクセスする際に使用するものです。 (詳細は https://docs.microsoft.com/en-us/windows/win32/sysinfo/handles-and-objects にて解説されています。)

上で紹介した IUnknown とは、全ての COM インタフェースの基底となるインタフェースです。今回作成した DLL においてはこれを継承した IDeskBand2 などのインタフェースを実装したクラスをしています。このインタフェースにおいては先に挙げた機能を実装するための関数が宣言されています。

今回作成したような、 COM server を公開する DLL は、 CLSID に基づいてオブジェクトを作成する関数をエクスポートし、クライアントがオブジェクトを利用できるようになっています。

その他のプログラムの実装

先述の通りですが、 DeskBand を実装した DLL 以外にも2つのアプリケーションが一緒に使われています。

1つは各種設定を行うためのGUIアプリケーションで、 Twitter のアクセストークンなどを格納する設定ファイルに設定を保存します。

2つ目は DeskBand 側で入力された文章を実際にツイートするプログラムです。DeskBand 側とは名前付きパイプで通信しています。このプログラムはユーザーが Windows にサインインした際に自動的に開始するようになっています。

まとめ・参考資料

この記事ではタスクバー上からツイートするプログラムを開発したこと、およびそれに使用した技術を紹介しました。もちろんここで紹介した技術以外にも様々な技術を使っていますので興味のある方はネットで検索したり、ソースコードを見てみたりしてください。 (ややコードが汚くて申し訳ないですが….)

また、このプログラムを開発するにあたってはマイクロソフト公式のドキュメント意外にも http://eternalwindows.jp/ のページを参考にています。興味のある方はこちらも覗いてみてはいかがでしょうか。

さて、明日は @honai くんの記事です。一体どんな記事を書いてくれるのか、楽しみですね!

それでは〜