• クラークの第三法則

コンピューターは、デジタル回路、あるいは論理回路で作られていて、複雑ではあるが、その振る舞いは予測可能であり曖昧な部分や不明な部分はない。しかし、オペレーティングシステムのような複雑なソフトウェアが動き、さらにアプリケーションが複数同時に動くとき、システムとしての実行結果を簡単に予測できなくなる。ソフトウェアが絡むとコンピュータはあまりに複雑になってしまうからだろう。

筆者は、こうした問題は、巨大なAIシステムやオペレーティングシステムの開発のような場合のことで、自分の身近にある問題とは思っていなかった。しかし、最近、Windowsのプログラムを作っていて、思いもよらない「複雑」な問題に直面した。

筆者がやろうとしていたことは、エディタのようなプログラムでユーザーが「Ctrl+C」を押したのと同じことを別プログラムから行なうことだ。Ctrl+Cは、テキストを扱うほとんどのプログラムやテキスト入力欄で、選択範囲をクリップボードにコピーするキーだ。もちろん、対象はメモ帳のようなテキストの選択やクリップボードのコピーが可能なプログラムで、任意のプログラムを想定しているわけではない。

.NET Frameworkでは、“Sendkeys.sendwait”というメソッドを使うことでキーを押したのと同じことを行える。“^c”などのキーを示す文字列(これがCtrl+Cを表わす)を引数として渡してやると、アクティブなアプリケーションにキーコードが送られる。

メモ帳のようなプログラムで選択範囲があれば、クリップボードに選択した範囲がコピーされるはずなのだが、何も起こらない。最初は、筆者のプログラムが起動したことで、対象のプログラムがもうアクティブではないのかと疑った。調べて見るとアクティブなウィンドウはそのままの状態だった。

キーコードを送信できるWin32APIのSendInputという関数を使ってみる、“WM_COPY”というメッセージを送るなども試してみた。しかし、どうやってもクリップボードに選択範囲が入ってくれない。その他にも色々やってみたが何をやってもダメ、どうしようもなくてインターネット検索してみた。

筆者同様、うまくコピーできなかった人が多かったらしく、あちこちのサイトに質問や相談があった。しかし、ほとんどの回答は、Windows 10/11でうまく動かなかった(古い記事もあった)。唯一、うまくいったのは、Sendkeysの前後で400ミリ秒停止するという何とも奇妙な回答だった(注01)。具体的には、以下のようなものだ。


Thread.Sleep(400);      # 400ミリ秒実行を停止
SendKeys.SendWait("^c");    # Ctrl+Cを送信
Thread.Sleep(400);      # 停止。この後クリップボードの内容取得
注01
c# - Copy and Modify selected text in different application - Stack Overflow(英語)
https://stackoverflow.com/questions/235972/copy-and-modify-selected-text-in-different-application

つまり、400ミリ秒プログラムを停止(Sleep)して、Ctrl+Cを送り、そのあとまた400ミリ秒プログラムを停止するというものだ。このプログラムだと、メモ帳やエディタアプリなどで選択している範囲がクリップボードに入る。

SendKeys.SendWaitは、「特定のキーをアクティブなアプリケーションに送信し、メッセージが処理されるまで待機します」とMicrosoftのサイトに解説がある(注02)。なので、キーを送ったあと、Sleepを入れることは試してみたがダメだった。しかし、前後にスリープを入れるとウソのように問題が解決した。

注02
SendKeys クラス (System.Windows.Forms) | Microsoft Learn
https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.sendkeys

この400ミリ秒という数字は根拠の不明な「魔法」の数字である。広い範囲のハードウェアで試してみたわけではないが、350ミリ秒では問題ないが200ミリ秒では失敗する。400ミリ秒よりも長くしてもいいのだが、合計800ミリ秒止まっているので、これ以上、長くするとプログラムの反応が遅くなる。どうもこのあたりが落としどころという感じだ。

クリップボードを着想し、コピーをCtrl+Cに割り当てたラリー・テスラー(Larry Tesler)は、「複雑さ保存の法則」(Law of conservation of complexity。Tesler's Lawとも)を提唱している。ソフトウェアには一定量の複雑さがあり、開発者がソフトウェア内で解決するか、あるいはユーザーが操作で解決するか、のどちらかで行なう必要があるというものだ。

最初は、ユーザーにテキストをクリップボードにコピーしてからプログラムを起動してもらうことを考えていたのだが、ユーザーがやるのは範囲選択までとしてコピーを自分のプログラムでやろうとして複雑さに遭遇した。単純な所から複雑な問題が出て複雑さが保存されていないようだが、ユーザーのキー入力を処理していたWindowsに何か複雑なものが隠れていたのだと想像している。

今回のタイトルネタは、アーサー・C・クラークの「十分に発達した科学技術は、魔法と見分けがつかない」(Any sufficiently advanced technology is indistinguishable from magic)である。Windowsもこの域に達したのかも。