TodoListを自動配信するソフトを作るために.NETとソフトウェアパッケージ化で詰まったよ

#　前書き
初心者脱却のために、どうしてもGUIプログラムを作りたい・・・！と思ったのとせっかくなので、TodoListdonをグレードアップしてみようと思いました

[電気系プログラミングガチ素人学生が初心者脱却のためのコアのプログラムを作ったら思いっきり脱線した話](https://qiita.com/sanbongazin/items/a5a75039a23cf91c6535)

#　この記事で伝えたいこと
* 干渉関係は解決が案外難しい
* 人に提供するときはインストーラー使うと便利やで？
* 初心者脱却はできてるのか・・・？

#話はいい。ソースはどこだ？
こちらに。
https://github.com/sanbongazin/TodoListDon-GUI

# 先ずは設計だ
コマンドで設計した内容ができているのだから、その内容をGUIに落とし込むだけ！と考えていたのがアホの極みでした。
画面の置き方をデザインビューで行います。今回はフォームアプリを利用しています。

#　画面遷移ってどうすればいいのよ・・・・
まず設計からつまづきました。コマンドプロンプトではクラス化すればいいだけだもの・・・ウィンドウどうすれば動くんや！！！って思ったら簡単にわかりました。

```C#
form2.ShowDialog();
```
これで表示できるし、閉じられるまで待ってくれるので、エラーが起きづらい！！めっちゃ楽！！！

# 追加は手軽にできるように
TodoListをまずは追加したいだろ！って思いながら、まずは、リストを作ってそれを追加します。また、TodoListを裏方でListに格納しています。

```C#
        private void ADD_button_Click(object sender, EventArgs e)
        {
            string text_value = Add_Text.Text;
            //TodoList.Add(text_value);
            TodoList_GUILIST.Items.Add (text_value);
            Add_Text.Clear();
            TodoList = this.TodoList_GUILIST.Items.Cast<string>().ToArray();
        }
```

#Todoの一通りの機能をどうやって実現するのか。
追加、削除はListBoxで追加や、削除は一行で済みますが、問題は保存と読み込み。そこで、裏でリストを格納させ、リストとデータをリンクさせ、テキストファイルとして保存させることで、保存、読み込みを可能としています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/244181/c5984c1e-aa24-0d1b-f2e2-012ba8d50eb2.png)

#なぜ必要？
保存の際にリストにしておくことで、foreach処理で一度文字数を連結することを可能にしています。

```C#
        public void Toot() {
            registeredApp = ApplicaionManager.RegistApp(host, "TodoListDon", Scope.Read | Scope.Write | Scope.Follow).Result;
            var client = new MastodonClient(host, AccessToken);

            UserName = HashTagOption.Text;

            int i = 0;
            var TodoString = ""; 
            foreach (var s in TodoList) {
                i++;
                TodoString += i + ":[ ]" + s + Environment.NewLine;
            }
            client.PostNewStatus(status: TodoString +"#"+ UserName +"_On_TodoListDon");
        }
```

思いつく簡単な処理がこれでしたので、今回はこの形にしてみました。

#トークンどうやって管理しようか
Mastdonで、トゥートするにも権限を付与したアクセストークンが必要です。そこで以前はテキストに裸で格納しているという危険な手法をとっていたので、それらを一般用で頒布するのはとんでもない方法なのでやめました。そこで、Qiitadonの方々に、レジストリを使ってみては？という案をいただきました。

<iframe src="https://qiitadon.com/@manzyun/101362917452673593/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="400">
</iframe><script src="https://qiitadon.com/embed.js" async="async"></script>

そこで、使ってみることにしました。

```C#
            try
            {
                //以下の行でレジストリを取り扱う
                Microsoft.Win32.RegistryKey regkey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"Software\TodoListDon\sub", false);
                //読み込む
                AccessToken = (string)regkey.GetValue("AccessToken");
                host = (string)regkey.GetValue("hostname");
                Toot();
            }
            catch (NullReferenceException e)
            {
                    form2.FormClosed += new FormClosedEventHandler(Form2_FormClosed);
                    try
                    {
                        form2.ShowDialog();
                    }
                    catch (Exception)
                    {
                        form2.Visible = false;
                    }
            }
```

tryセクションで、レジストリを読みに行きます。今回はSoftwareにkeyを作成しました。そこでhostの情報と、アクセストークンを取得するようにしました。また、もし取得できない場合、catchします。新規作成の場合はダイアログを作成します。ダイアログのソースコードは以下のようになります。この認証のソースコードは結構再利用できます。


```C# 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Mastodot.Enums;
using Mastodot.Utils;
using Mastodot.Entities;
using Mastodot.Exceptions;
using Microsoft.Win32;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

namespace WindowsFormsApplication1
{
    public partial class Oauth : Form
    {
        public string host;
        public Mastodot.Entities.RegisteredApp registeredApp;
        public string code;
        private MainWindow main = new MainWindow();

        public Oauth()
        {
            InitializeComponent();
        }

        public void GenerateButton_Click(object sender, EventArgs e)
        {
            host = InstanceInput.Text;
            try
            {
                registeredApp = ApplicaionManager.RegistApp(host, "TodoListDon", Scope.Read | Scope.Write | Scope.Follow).Result;
            }
           catch (AggregateException) {
                MessageBox.Show("正しい値を入力してください。",
                "エラー",
                MessageBoxButtons.OK,
                MessageBoxIcon.Error);
            }
            var url = ApplicaionManager.GetOAuthUrl(registeredApp);

            OAuthlink.Text = url;
        }

        private void OAuthlink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            //リンク先に移動したことにする
            OAuthlink.LinkVisited = true;
            //ブラウザで開く
            System.Diagnostics.Process.Start(OAuthlink.Text);
        }

        private async void OAuthButton_ClickAsync(object sender, EventArgs e)
        {

            // 操作するレジストリ・キーの名前
            string registryKeyName = @"Software\TodoListDon\Sub";
            // 取得処理を行う対象となるレジストリの値の名前
            string registryValueName = "hostname";

            //キー（HKEY_CURRENT_USER\Software\Sample）を開く
            using (RegistryKey registryKey = Registry.LocalMachine.CreateSubKey(registryKeyName))
            {
                // レジストリの値を設定
                registryKey.SetValue(registryValueName, host);
            }


            code = OAuthInput.Text;
            var tokens = await ApplicaionManager.GetAccessTokenByCode(registeredApp, code);



            // 取得処理を行う対象となるレジストリの値の名前
            registryValueName = "AccessToken";

            //キー（HKEY_CURRENT_USER\Software\Sample）を開く
            using (RegistryKey registryKey = Registry.LocalMachine.CreateSubKey(registryKeyName))
            {
                // レジストリの値を設定
                registryKey.SetValue(registryValueName, tokens.AccessToken);
            }

            Close();
        }

        private void Cancel_Button_Click(object sender, EventArgs e)
        {
            Close();
        }
    }
}
```

簡易的ですが、これでレジストリに記録が可能になります。テキストボックスにリンクを入力し、アクセストークンを取得します。アクセストークンは、レジストリに格納されます。


#さて、完成したぞ
コンパイルしようと思ったら何故かこんなエラーが・・・
![image.png](https://qiita-image-store.s3.amazonaws.com/0/244181/c9fdeefc-287b-80c9-af22-03bd2aacc171.png)

なにこれ？？？と思いつつ、検索しても出てこない。どうすればええんや・・・と思ったのですが、VS2017にするとなぜか実行可能に・・・

#ソフトウェアが頒布されるために・・・
ソフトウェアを頒布したいならやはりインストーラは必要ではと感じたため、どうやってインストーラを作ろうかと考えました。しかし、VisualStudioならそういったパッケージも簡単に作成可能のようです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/244181/dfe63ea7-3516-4511-3fd4-4c6c82bd9201.png)

拡張機能からダウンロード可能です。さて、これを活用しましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/244181/e87e9869-1863-304a-2b4e-5d4363283394.png)
ソリューションを右クリックして、新しいプロジェクトを追加しましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/244181/88a4f5f5-d4af-e041-16f9-f1fa86620e72.png)
その他のプロジェクトにセットアップを追加できます。

そうすると3つのフォルダーが出現します

![image.png](https://qiita-image-store.s3.amazonaws.com/0/244181/8163eb5c-95af-1518-1242-ee13aa75e50c.png)

ApplicationFolderを右クリックすることで、メニューを開き、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/244181/c841297b-794a-28e1-257c-3ae0ed0d1e64.png)

プロジェクト出力をクリックしましょう。これで出力されたソフトウェアをインストールパッケージに加えることができます。ソフトウェアをデスクトップやスタートメニューに加えることも可能です。その際は以下のようにショートカットを作り、それぞれのフォルダーに入れましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/244181/365cbbac-6ff7-6ca9-dcbd-11dcab951d7c.png)

また、PDFをフォルダーに追加することも可能です。これにより、説明書なども入れることができます。

これで初めてのソフトウェアづくりは完遂できました・・・・

# これで学びになったの？
はじめて初心者向けのチュートリアルを一つクリアできたというところでしょうか・・・学んだことは

* ソフトウェア設計はコンソールベースで作成すると、バックグラウンドでの処理を意識しやすい
* GUIとCUIではソフトウェアの管理の煩雑さが段違い
* それでもC#はかなり設計を形にしやすい言語でもあると感じる
* APIをたたくときは、中身のソフトウェアの動作を見ることも重要である
* 構築するIDEは最新版を使おうね
* ソフトウェアパッケージは意外に作成が簡単だよ 　

あたり前なものもあるかもしれません。しかし、電気系プログラムガチ素人である私にはとても新鮮なことも多かったです。また、コーディングもまだまだ非効率であったり、穴もありそうです。まだまだ学ぶことは多そうですね・・・

# 次回の課題は
今度は動作OSを拡大したいところです。また、ソフトウェアデザインの必要性を強く感じました。あとは・・・フォームじゃなくてXamlで作りたくなりました・・・できるだけ新しい技術に触れていたいところです。

#参考文献
Visual Studio 2017でインストーラ作成
https://www.osadasoft.com/visual-studio-2017%E3%81%A7%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%A9%E4%BD%9C%E6%88%90/