VisualStudio UWP、C#、C++ で特別なフォルダ（AppData）にファイルを保存する

## C#、C++で特別なフォルダ（AppData以下）にファイルを保存したい！
Pythonで作ったWindowsアプリの性能をあげたいと思い、VisualStudio2022で、C++、C#を用いてWindowsアプリを再構築しようとしています。
C#やC++はどちらも初めてで、VisualStudioでのWindowsアプリの開発も初めてです。
詳しい方から言えば、"何を当たり前のことを"という内容かもしれませんが、これだけでざっくり8時間以上は潰したので、記念に残しておきます。

## WindowsアプリはAppData以下にアプリの設定ファイルなどを保存するらしい
開発しているWindowsアプリでは、各クライアントごとに作成しなければならないファイル（TensorRTのengineファイル）があり、それを各クライアントで保存しなくてはならない。
Windowsでは`C:\Users\ユーザ名\AppData\`以下に自分のアプリフォルダを作成し、設定ファイルなどを保存するという。
`AppData`は隠しフォルダで、フォルダを開いてみると確かに配下にはいろんなアプリのフォルダと思しきものが並んでいる！
"ここに私のアプリも並ぶのかー。キャー（嬉しい）"
GUIはC#で作成しており、Windows Copilotに相談して、次のようなコードで`AppData`配下にフォルダを作ろうとしました。
`WoLNamesBlackedOut`は私のアプリ名です。
大量に不要なデバッグ用のコードがたくさんついているのは、私が苦労した証拠です。

```c#:C#（ダメだった例）
try
{
    // AppDataのパスを取得
    string localAppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
    // アプリケーション専用のフォルダを作成
    string appFolder = System.IO.Path.Combine(localAppDataPath, "WoLNamesBlackedOut");
    System.IO.Directory.CreateDirectory(appFolder);
    // フォルダが正しく作成されたかを確認
    if (Directory.Exists(appFolder))
    {
        Console.WriteLine($"Folder created successfully: {appFolder}");
    }
    else
    {
        Console.WriteLine($"Failed to create folder: {appFolder}");
    }
    // さらに詳細なデバッグ情報を表示
    DirectoryInfo dirInfo = new DirectoryInfo(appFolder);
    Console.WriteLine($"Directory Full Name: {dirInfo.FullName}");
    Console.WriteLine($"Directory Exists: {dirInfo.Exists}");
    Console.WriteLine($"Directory Attributes: {dirInfo.Attributes}");
}
catch (Exception ex)
{
    Console.WriteLine($"Exception occurred: {ex.Message}");
}
```
ブレイクポイントを利用して、1行ずつ確認します。
うん、作ってる！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679605/c0bde931-394e-499a-1c17-a46b950f6407.png)

C++で作ったDLLからも書き出し出来てるし、大丈夫ぽいな！
一応作ったファイルのツラを拝んどくか

・・・作ったはずのフォルダが無い！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679605/3ca2769b-6287-77f8-8706-d1131845bb1b.png)

## 自分が作っているものはUWPアプリだった！
何を言っているのかと思われるかもしれませんが、私が作っているアプリはUWPアプリだったようです。
Microsoftストアにも公開できたらいいなと思っていたので、できるだけWindowsの最新の作り方がいいだろうとは思って、WinUI3などを採用していましたが、それはUWPアプリという扱いのものだったようなのです。

https://learn.microsoft.com/ja-jp/windows/uwp/get-started/universal-application-platform-guide

UWPアプリはパッケージというものになっており、上記の特別なフォルダは`C:\Users\ユーザ名\AppData\Local\Packages`以下になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679605/d458c8a7-90a7-2737-20c9-708d7a60ab5d.png)

アプリ単位（？）に、暗号のような長い名前のフォルダがあり、その配下に下図のような特別なフォルダが存在します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679605/b755cf5c-46a4-8523-932d-d3d6588616b8.png)

なお、先ほどブレイクポイントで確認しながら作ったと思っていた`WoLNamesBlackedOut`のフォルダは、ここの`LocalCache`の下の`Local`にありました。
じゃあそこに作ったよってVS2022には言って欲しいところだけど、きっとWindowsが気を利かせてそのように振る舞ってくれたのでしょうし、あまり大きな声で文句も言えません。
`LocalCache`がずっと使えるデータなら良かったのですが、一時的なデータを保存する場所のようなので、ずっと使える`LocalState`の方にフォルダを作って、そこにデータ保存する方針としました。
アプリ起動時にフォルダを作るとして、C++で作ったDLLは、C#からフルパスを渡すか直接アクセスするか悩んだのですが、C++から直接アクセスする方法としました。

### C#で`LocalState`にフォルダを作成する
これは簡単でした。
```c#:C#
try
{
    // LocalStateのパスを取得
    StorageFolder localFolder = ApplicationData.Current.LocalFolder;
    string localAppDataPath = localFolder.Path;

    // アプリケーション専用のフォルダを作成
    string appFolder = System.IO.Path.Combine(localAppDataPath, "WoLNamesBlackedOut");
    if (!Directory.Exists(appFolder))
    {
        Directory.CreateDirectory(appFolder);
        Console.WriteLine($"Folder created successfully: {appFolder}");
    }
    else
    {
        Console.WriteLine($"Folder already exists: {appFolder}");
    }
}
catch (Exception ex)
{
    Console.WriteLine($"Exception occurred: {ex.Message}");
}
```

## C++で`LocalState`に作成したフォルダにファイルを保存する。
こっちはよくわからなくて大変でした。
下のコードが最終形です。
`my_yolov8m.engine`は保存するファイル名です。

```c++:C++
#include <Windows.Storage.h>
#include <Windows.Foundation.h>
#include <Windows.h>
#include <winrt/Windows.Storage.h>

using namespace winrt::Windows::Storage;

// ローカルフォルダのパスを取得
auto localFolder = ApplicationData::Current().LocalFolder();
std::wstring localAppDataPath = localFolder.Path().c_str();

// アプリケーション専用のフォルダパスを組み立てる
std::wstring appFolderPath = std::wstring(localAppDataPath) + std::wstring { L"\\WoLNamesBlackedOut" }; 
std::wstring engineFilePath = appFolderPath + std::wstring{ L"\\my_yolov8m.engine" };

std::string engineFilePathStr(engineFilePath.length(), 0);
std::transform(engineFilePath.begin(), engineFilePath.end(), engineFilePathStr.begin(), [](wchar_t c) {
    return (char)c;
    });
const char* engineFilePathCStr = engineFilePathStr.c_str();
```

### C++では、まずはNuGet `Microsoft.Windows.CppWinRT`！
C++で`LocalState`にアクセスするにはどうしたら？
と検索すると下記が見つかりました。

https://stackoverflow.com/questions/54759816/uwp-how-to-access-local-data-directory

ふむふむ。
`using namespace Windows::Storage;`
を使えば
`const wchar_t* wPath = localFolder->Path->Data();`
と。
で、早速`using namespace Windows::Storage;`って書いてみたんですが、`Windows`に`Storage`は無いよ？と言われます。
ええ？`#include <Windows.Storage.h>`してるけど・・？

https://learn.microsoft.com/ja-jp/uwp/api/windows.storage.applicationdata?view=winrt-26100

`#include <winrt/Windows.Storage.h>`って書くのかな？と思って書くと、VS2022は`winrt`なんぞ知らんと出る。
あれこれ調べると、どうやら先に`Microsoft.Windows.CppWinRT`をnugetで入れないといけないらしい。

https://github.com/microsoft/cppwinrt/issues/866

https://www.nuget.org/packages/Microsoft.Windows.CppWinRT

### wstringからcharにしたい
下記の2番目の記事が参考になりました。

https://stackoverflow.com/questions/4804298/how-to-convert-wstring-into-string

Windowsでは、`WideCharToMultiByte`というものもあるらしいです。

https://learn.microsoft.com/ja-jp/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte

## 終わりに
ファイル保存するだけで1日以上試行錯誤してしまいました。
WindowsアプリはWPF、UWP、WinRTと最近いろいろとフレームワークが移ろったためか、情報がまとまってなく、特にWinRT/C++はかなりハードルが高いと感じました。
WindowsアプリはC#で作ってね、ということなのかもしれませんが、凝ったことをしようとするとC++を使わざるを得ず、そのC#とC++の境界となるインターフェース部分にはいろいろノウハウがありそうだなーと感じました。