UnhookWindowsHookEx()を呼び出す必要は厳密にはありません。Windowsは、呼び出すのを忘れたことを認識し、プログラムが終了するとフックを解除します。必然的に、フックをクリーンアップしないと、Windowsがハングします。オペレーティングシステムに任せないことは「マナー」と見なされます。
Application.Run()呼び出しは、低レベルのフックを実装するプログラムの厳しい要件です。メッセージループがない場合、たとえばConsole.ReadLine()を代わりに使用すると、フックへのコールバックは行われません。特定のWindowsの約束は、コールバックがSetWindowsHookEx()を呼び出したのと同じスレッドで行われることです。これを行うために、Windowsにはどういうわけか「侵入」があり、スレッドにコールバックメソッドを呼び出すように強制します。スレッドを勝手に中断して強制的に呼び出しを行うことはできません。これは、恐ろしい再入可能性の問題を引き起こします。スレッドは明確に定義された状態である必要があり、アイドル状態であり、プログラムの状態を変更していない必要があります。
メッセージループ、特にGetMessage()またはPeekMessage()winapi関数はそのシグナルであり、スレッドがメッセージループをポンピングすると、アイドル状態になり、Windowsが何かを実行するように指示するのを待機します。これは、生産者/消費者問題に対する普遍的な解決策です。