1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2025-01-23

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#(ダメだった例)
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

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

・・・作ったはずのフォルダが無い!
image.png

自分が作っているものはUWPアプリだった!

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

UWPアプリはパッケージというものになっており、上記の特別なフォルダはC:\Users\ユーザ名\AppData\Local\Packages以下になります。
image.png

アプリ単位(?)に、暗号のような長い名前のフォルダがあり、その配下に下図のような特別なフォルダが存在します。
image.png

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

C#でLocalStateにフォルダを作成する

これは簡単でした。

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++
#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にアクセスするにはどうしたら?
と検索すると下記が見つかりました。

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

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

wstringからcharにしたい

下記の2番目の記事が参考になりました。

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

終わりに

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

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?