【自動Ctrl+S】を定期的に行うWindowsアプリケーションを作る【卒論クラッシュ対策】

# はじめに

卒業論文の季節ですね．この時期はいきなりPCが落ちて作業内容が保存されていなかった！なんて悲鳴が各地から聞こえてきます．その対策をします．

[総コン AdventCalender 2024](https://adventar.org/calendars/10574) 9日目の記事です．

https://adventar.org/calendars/10574

# 本記事を読んで得られるもの
- 現在開いているソフトウェアに，指定した間隔ごとに自動的に保存(Ctrl+S)を行ってくれる Windows コンソールアプリケーションを作成します

# 記事の流れ
- Windows コンソールアプリケーションの作成
- スタートアップに常駐させて，PC起動時に自動的に起動するようにする
- スタートメニューの候補に出てくるようにして再度アプリを起動しやすくする

# 環境
- Visual Studio 2022
- Windows 10
- .Net 8.0

# Windows コンソールアプリケーションの作成

## Visual Studio のインストール
Visual Studioの環境がない場合は，[Visual Studioをインストール](https://visualstudio.microsoft.com/ja/downloads/)します．
本記事では，Visual Studio 2022で進めていきます．

## Visual Studio のプロジェクトの作成
Visual Studioを開いて，新しいプロジェクトの作成を押します．

検索窓に"コンソール"と打ち込んで，出てきたコンソールアプリをクリックします．
![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1865650/30d465c0-d5df-740d-c801-4e1bc6d82a9c.png)

プロジェクト名は，AutoSaveなどでいいでしょう．

## コードの記述
最初に書いてある Hello World を消して，以下のコードをコピペして記述します．

```AutoSave.cs
using System.Timers;
using System.Runtime.InteropServices;

static class Program
{
    [DllImport("user32.dll")]
    private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);

    private const int KEYEVENTF_KEYDOWN = 0x0000;
    private const int KEYEVENTF_KEYUP = 0x0002;
    private const byte VK_CONTROL = 0x11;
    private const byte VK_S = 0x53;

    private const int INTERVAL_MILI_SECONDS = 5000; // 何ミリ秒ごとに保存するか
    private const ConsoleKey QUIT_KEY = ConsoleKey.Q; // アプリケーションを終了するキーの指定

    private static System.Timers.Timer timer;

    [STAThread]
    static void Main()
    {
        Console.WriteLine("AutoSaveApp started. Press q to stop.");

        // Timerの設定
        timer = new System.Timers.Timer(INTERVAL_MILI_SECONDS);
        timer.Elapsed += OnTimerElapsed;
        timer.AutoReset = true;
        timer.Start();

        // 終了するキー入力があるまで待機
        while (Console.ReadKey().Key != QUIT_KEY)
        {
        }
    }

    private static void OnTimerElapsed(object sender, ElapsedEventArgs e)
    {
        SimulateCtrlS();
    }

    private static void SimulateCtrlS()
    {
        // Ctrl + S キーの押下
        keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYDOWN, 0);
        keybd_event(VK_S, 0, KEYEVENTF_KEYDOWN, 0);
        // Ctrl + S キーの押上
        keybd_event(VK_S, 0, KEYEVENTF_KEYUP, 0);
        keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);

        Console.WriteLine("Ctrl+S pressed at: " + DateTime.Now);
    }
}
```

何ミリ秒ごとに保存するかについては，テストのために短い間隔にしておきましょう．
実際に問題なく実行できた場合は，5分(6000*5)などの間隔にするとよいでしょう．
```cs
private const int INTERVAL_MILI_SECONDS = 1000*5; // 何ミリ秒ごとに保存するか
```

## 解説

Ctrl+Sを押したことをWindowsに伝えるために，[keybd_event](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-keybd_event)を用います．
これは任意のキーの入力をシミュレートしてくれるAPIです．
[System.Runtime.InteropServices.DllImport](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.dllimportattribute?view=net-8.0)を用いて，.Netから呼び出せるようにしています．

また，一定秒数ごとに処理を行いたいので [System.Timers.Timer](https://learn.microsoft.com/ja-jp/dotnet/api/system.timers.timer?view=net-9.0)を用います．

実行後にキー入力を待機し続けることで，アプリケーションが終了しないようにします．

## ビルドしてexeファイルにする

コードを記述して保存できたら，メニューバーから ビルド->ソリューションのビルドをクリックします．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1865650/67a16d82-c9c3-d509-86e6-436d940e2ba3.png)
ビルドは .Net8.0 などでいいでしょう．

問題なくビルドできれば，アプリケーションの作成はこれで終了です．
デフォルトの設定では，以下のフォルダに作成されているはずです．
（UserNameや.Netのバージョンなどは適宜読み替えてください）
```
C:\Users\UserName\source\repos\AutoSave\AutoSave\bin\Debug\net8.0\AutoSave.exe
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1865650/020267b5-d512-5d9d-2dbc-b4bb1a2b4486.png)

AutoSave.exe をクリックして実行すると起動できます．
以下のようなログがでれば問題ないと思われます．

![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1865650/1b531fa5-1317-c538-989f-3f2da644398a.png)

起動できたら，実際にCtrl+Sが行われているかのテストを行いましょう．
例えばこのままブラウザを開くと，Ctrl+Sが押下されたタイミングでページを保存のようなウィンドウが出てくるはずなので，確認しやすいと思われます．

# スタートアップに常駐させる

先ほど作成した AutoSave.exe を右クリック
ショートカットの作成 を押してショートカットを作成します．

次に，新しいエクスプローラーを開き，
アドレスバーに shell:startup を入力してエンターキーを押してください．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1865650/2e4589b4-3ccd-326e-7d42-79a30c2ae034.png)

以下のフォルダに飛んだはずです．
```
C:\Users\UserName\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
```
このフォルダに先ほど作成した，AutoSave - ショートカット を移動させます．
これで作業は完了です．

タスクマネージャーを開いて，スタートアップ項目に
AutoSaveが追加されていれば問題なく設定できているはずです．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1865650/5986ce0a-5a8f-28e9-175d-44e51484ae3a.png)

再起動して自動でAutoSaveが起動することを確認してみましょう．
終了するには，AutoSaveコンソールアプリを選択した上で，Qキーを押します．

（参考：[パソコン起動時にアプリも起動させたい | 伊藤忠ケーブルシステム株式会社　エディティング・サポート・ボックス -EditingSupportBox-](https://itochu-cable-systems.jp/esb/category/useful/other/2023/03/6462/)）

# スタートメニューに追加する

また，Windowsキーを押して出てくるスタートメニューからもすぐに起動できるようにしておきます．一時的にアプリを終了したあと，再度すぐに起動できるようにしておきます．

もう一度，AutoSaveをショートカットを作成します．
エクスプローラーを開いて下記のパスに移動します．

```
C:\Users\UserName\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
```

:::note warn
デフォルトだと隠しファイル(AppData)が表示されません．
下記を参考に，隠しファイルを表示する設定にしておきましょう．
:::

（参考：[隠しファイルを表示する - Microsoft サポート](https://support.microsoft.com/ja-jp/windows/%E9%9A%A0%E3%81%97%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B-0320fe58-0117-fd59-6851-9b7f9840fdb2)）

エクスプローラーで上記のパスに移動できたら，先ほど作成した AutoSave.exe のショートカットをここに移動させます．これで作業は完了です．

Windowsキーを入力して，Auto くらいまで打ち込むとスタートメニューの候補に出てきてくれるようになったはずです．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1865650/ea874e49-9a24-0170-8091-10767bb6e347.png)

（参考：[【Windows 10】スタートアップで起動時に実行されるアプリを追加・削除する：Tech TIPS - ＠IT](https://atmarkit.itmedia.co.jp/ait/articles/1701/18/news029.html)）

# 保存間隔の調整
問題なく自動起動＋保存されていることが確認出来たら，保存間隔を好きな値にしておきましょう．再度ビルドすれば問題なく動くはずです．ショートカットはexeファイルのパスを記憶しているので，スタートアップやスタートメニューの設定を再度行う必要はありません．

# 自動保存を無効化したい場合
タスクマネージャーを開いて，スタートアップの項目を開き，AutoSave を右クリックして 無効化 をクリックすれば簡単に ON OFF ができます．

# おわりに
これで保存できていなかった！なんてことが起こる可能性が低くなりました！
ただ，PC自体が壊れる可能性は依然としてあるのでバックアップを取っておきましょう...

# 参考サイト
- [keybd_event関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-keybd_event)
- [DllImportAttribute クラス (System.Runtime.InteropServices) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.dllimportattribute?view=net-8.0)
- [Timer クラス (System.Timers) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.timers.timer?view=net-9.0)
- [パソコン起動時にアプリも起動させたい | 伊藤忠ケーブルシステム株式会社　エディティング・サポート・ボックス -EditingSupportBox-](https://itochu-cable-systems.jp/esb/category/useful/other/2023/03/6462/)
- [隠しファイルを表示する - Microsoft サポート](https://support.microsoft.com/ja-jp/windows/%E9%9A%A0%E3%81%97%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B-0320fe58-0117-fd59-6851-9b7f9840fdb2)
- [【Windows 10】スタートアップで起動時に実行されるアプリを追加・削除する：Tech TIPS - ＠IT](https://atmarkit.itmedia.co.jp/ait/articles/1701/18/news029.html)