質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.21%

Xamarin C# で Directory.Existsの戻り値が変わってしまいます

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 372

as00000

score 10

前提・実現したいこと

VisualStudio2019 Xamarin で Directory.Exists を使用して
フォルダの存在確認をしているのですが
間違いなく存在しているフォルダのチェックを行なっても
ViewModelからアクセスすると"False"が返って来てしまいます
(Testコードからアクセスすると"True"となるのに)

何故、この様な挙動になるのか
同じ結果が返る様にするには、どこを直せば良いのかが全く分かりません

ご教示をお願いします


※4/1追記
DirectoryInfo directoryInfo = new DirectoryInfo(targetFolderPath);

                if (Directory.Exists(targetFolderPath))
                  ~


Directory.Existsの前でDirectoryInfoを確認してみた所
イメージ説明
'System.UnauthorizedAccessException' の例外が出ていました
(フォルダ名は事情により少し変えていますが、フォルダは間違いなく存在します)

Windowsは管理者権限で立ち上げていますし、VisualStudioを管理者権限で起動しても
結果は変わりませんでした

このエラーのせいでFalseが返って来ているのだと思いますが
エラーの原因が分かりません


※4/3追記

f-miyuさんに依頼頂いたので
変更後の Package.appxmanifest と UWP側で書いたコードを追記します

<?xml version="1.0" encoding="utf-8"?>

<!--<Package
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
  IgnorableNamespaces="uap mp">-->

<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap mp rescap">

...

  <!--<Capabilities>
    <Capability Name="internetClient" />
  </Capabilities>-->

  <Capabilities>
    <rescap:Capability Name="broadFileSystemAccess" />
  </Capabilities>

</Package>

↓とりあえず動作確認の為に、項目の少ないインターフェースを作成しました

[assembly: Dependency(typeof(DataOperatorUWP))]
namespace PlaceTheProcessingData.UWP
{
    class DataOperatorUWP : IDataOperator
    {
        public async Task<TargetFiles> GetAllSourceDataAsync(DirectoryPath sourcePath)
        {
             // 『sourcePath.Main = public List<string> GetSourceFiles(string targetFolderPath)に
             // 渡していた targetFolderPathです』
            var folder = await StorageFolder.GetFolderFromPathAsync(sourcePath.Main); //←【ここから進みません】
            var files = await folder.GetFilesAsync();

            return new TargetFiles();

        }

        public TargetFiles GetSpecificedCodeData(string projectCode, DirectoryPath sourcePath)
        {
            throw new NotImplementedException();
        }
    }
}


↓インターフェースの定義

    public interface IDataOperator
    {

        Task<TargetFiles> GetAllSourceDataAsync(DirectoryPath sourcePath);
        TargetFiles GetSpecificedCodeData(string projectCode, DirectoryPath sourcePath);

    }

↓実際にUWP側を呼び出している部分

        public void GetFileInfo()
        {

            var dataOperator = DependencyService.Get<IDataOperator>();
            this.SourceAllData = dataOperator.GetAllSourceDataAsync(this.SourcePath).Result;

            this.SourceAllData = this.dataOperator.GetAllSourceData(this.SourcePath);
            this.SourceSpecificedCodeData = this.dataOperator.GetSpecificedCodeData(this.Code, this.SourcePath);
            this.DestinationSpecificedCodeData = this.dataOperator.GetSpecificedCodeData(this.Code, this.DestinationPath);
            this.SaveSpecificedCodeData = this.dataOperator.GetSpecificedCodeSaveFolders(this.Code, this.SavePath);

該当のソースコード

Testからメソッドを呼び出している部分(希望通りTrueになるパターン)

[TestMethod()]
        public void GetFileInfoTest()
        {
            Project project = new Project(new DataOperator());

            project.Code = "10025";
            project.GetFileInfo();

以下 略

ViewModelからメソッドを呼び出している部分(Falseになってしまうパターン)

public class ProjectViewModel : BindableBase, IDisposable
    {
        private CompositeDisposable Disposable { get; } = new CompositeDisposable();
        public Project ProjectModel { get; }


        [CodeValidation]
        public ReactiveProperty<string> Code { get; set; } = new ReactiveProperty<string>();
        public ReactiveProperty<string> Name { get; set; } = new ReactiveProperty<string>();
        public ReactiveProperty<string> CodeAndName { get; set; } = new ReactiveProperty<string>();

        public ReactiveProperty<string> SourceStatusInfo_Main { get; set; } = new ReactiveProperty<string>();

        public ReactiveCommand ClearInputCommand { get; private set; } = new ReactiveCommand();
        public ReactiveCommand RefreshInfoCommand { get; private set; } = new ReactiveCommand();


        public ProjectViewModel(Project projectModel)
        {
            this.ProjectModel = projectModel;


            this.Code = projectModel.ToReactivePropertyAsSynchronized(x => x.Code).SetValidateAttribute(() => Code); 
            this.Name = projectModel.ToReactivePropertyAsSynchronized(x => x.Name);
            this.CodeAndName = projectModel.ToReactivePropertyAsSynchronized(x => x.CodeAndName);


            ClearInputCommand.Subscribe(_ => projectModel.ClearInput());

            RefreshInfoCommand = Code.ObserveHasErrors.Select(x => !x).ToReactiveCommand();
            RefreshInfoCommand.Subscribe(_ => projectModel.GetFileInfo()); ←【ここで設定してます】

        }

        public void Dispose() => this.Disposable.Dispose();

    }

ViewModel・Testから呼び出しているメソッド

public void GetFileInfo()
        {
            this.SourceAllData = this.dataOperator.GetAllSourceData(this.SourcePath);
            this.SourceSpecificedCodeData = this.dataOperator.GetSpecificedCodeData(this.Code, this.SourcePath);
            this.DestinationSpecificedCodeData = this.dataOperator.GetSpecificedCodeData(this.Code, this.DestinationPath);
            this.SaveSpecificedCodeData = this.dataOperator.GetSpecificedCodeSaveFolders(this.Code, this.SavePath);

以下 略

GetAllSourceDataに渡している引数(Pathのstringをまとめただけのクラスです)

public DirectoryPath SourcePath
        {
            get
            {
                return new DirectoryPath()
                {
                    Main = "C:\\Users\\as\\Desktop\\データ\\Mainデータ出力",
                    Sub = "C:\\Users\\as\\Desktop\\データ\\Subデータ出力"
                };

            }
        }

上記 dataOperator.GetAllSourceData のコード

public TargetFiles GetAllSourceData(DirectoryPath sourcePath)

        {

            TargetFiles getAllSourceData = new TargetFiles()
            {
                Main = this.fo.GetSourceFiles(sourcePath.Main),
                Sub = this.fo.GetSourceFiles(sourcePath.Sub)
            };

更に呼び出されている fo.GetSourceFiles(Directory.Existsを使用している部分)

public List<string> GetSourceFiles(string targetFolderPath)
            {

                if (Directory.Exists(targetFolderPath)) ←【ここの返り値が変わってしまう】
                {
                    List<string> getSourceFiles = new List<string>();
                    getSourceFiles.AddRange(Directory.GetFiles(targetFolderPath));

                    return getSourceFiles;

                }
                else
                {
                    return null;
                }
            }

試したこと

SourcePathのパス文字がおかしいのかと
Main = "C:\\Users\\as\\Desktop\\データ\\Mainデータ出力"

Main = "C:\\Users\\as\\Desktop\\データ\\Mainデータ出力\\"
に変更

ProjectViewModel 内で
RefreshInfoCommand.Subscribe(_ => projectModel.GetFileInfo());

RefreshInfoCommand.Subscribe(_ => ProjectModel.GetFileInfo());
に変更
(参照が違ってしまっているのかと、引数からプロパティに変更してみた)

しましたが、変わりませんでした

補足情報(FW/ツールのバージョンなど)

Microsoft Visual Studio Community 2019
Version 16.4.5
VisualStudio.16.Release/16.4.5+29806.167
Microsoft .NET Framework
Version 4.8.03752

インストールされているバージョン:Community

Visual C++ 2019   00435-60000-00000-AA788
Microsoft Visual C++ 2019

ASP.NET and Web Tools 2019   16.4.460.23317

ASP.NET Web Frameworks and Tools 2019   16.4.460.23317

Azure App Service Tools v3.0.0   16.4.460.23317

Azure Functions and Web Jobs Tools   16.4.460.23317

C# ツール   3.4.1-beta4-19614-01+165046097562cfe65b09c2e9a9d8f7cd88526f2c

Common Azure Tools   1.10

Extensibility Message Bus   1.2.0 (d16-2@8b56e20)

F# 4.6 用 Visual F# Tools 10.4   16.4.0-beta.19556.5+e7597deb7042710a7142bdccabd6f92b0840d354

Microsoft Azure Tools   2.9

Microsoft Continuous Delivery Tools for Visual Studio   0.4

Microsoft JVM Debugger   1.0

Microsoft MI-Based Debugger   1.0

Microsoft Visual C++ ウィザード   1.0

Microsoft Visual Studio Tools for Containers   1.1

Microsoft Visual Studio VC パッケージ   1.0

Mono Debugging for Visual Studio   16.5.24 (1fafd7e)

NuGet パッケージ マネージャー   5.4.0

PHP Tools for Visual Studio   1.32.11706.2019

ProjectServicesPackage Extension   1.0

SQL Server Data Tools   16.0.62002.03150

StylerPackage Extension   1.0

TypeScript Tools   16.0.11031.2001

Visual Basic ツール   3.4.1-beta4-19614-01+165046097562cfe65b09c2e9a9d8f7cd88526f2c

Visual Studio Code デバッグ アダプターのホスト パッケージ   1.0

Visual Studio Tools for Containers   1.0

Visual Studio コンテナー ツール拡張機能 (プレビュー)   1.0

VisualStudio.DeviceLog   1.0

VisualStudio.Foo   1.0

VisualStudio.Mac   1.0

Xamarin   16.4.000.311 (d16-4@ddfd842)

Xamarin Designer   16.4.0.475 (remotes/origin/d16-4@ac250f5aa)

Xamarin Templates   16.4.25 (579ee62)

Xamarin.Android SDK   10.1.4.0 (d16-4/e44d1ae)
Xamarin.Android Reference Assemblies and MSBuild support.
Mono: fd9f379
Java.Interop: xamarin/java.interop/d16-4@c4e569f
ProGuard: xamarin/proguard/master@905836d
SQLite: xamarin/sqlite/3.28.0@46204c4
Xamarin.Android Tools: xamarin/xamarin-android-tools/d16-5@9f4ed4b

Xamarin.iOS and Xamarin.Mac SDK   13.10.0.17 (5f802ef)

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

UWPアプリは、アクセスできるファイルに制限があります。全ファイルのアクセス権限を付与するには、Package.appxmanifestを以下のように更新し、ビルドした後に、設定画面の[プライバシー] > [ファイル システム]から対象のアプリの許可をオンにしてください。

<Package
  ...
  xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
  IgnorableNamespaces="uap mp uap5 rescap">
...
<Capabilities>
    <rescap:Capability Name="broadFileSystemAccess" />
</Capabilities>

参照:ファイル アクセス許可

ただおそらくそれでも、Directory.Existsだとfalseを返すのではないかと思うので、UWPプロジェクトの方で、Windows.Storage.StorageFolderを用いてファイル操作をした方がいいかもしれません。

try
{
    var folder = await StorageFolder.GetFolderFromPathAsync(targetFolderPath);
    var files = await folder.GetFilesAsync(); 
} 
catch
{
}

dataOperator.GetAllSourceDataAsync(this.SourcePath).Resultは、現在のスレッドをブロックするので、await StorageFolder.GetFolderFromPathAsync(sourcePath.Main)の完了後に元のスレッドに戻ることができなくて、デッドロックが発生してしまいます。ConfigureAwait(false)で元のスレッドに戻らないようにするか、そもそもResultではなく、awaitを使うようにするかのどちらかの対応を行う必要があります。

        public async Task<TargetFiles> GetAllSourceDataAsync(DirectoryPath sourcePath)
        {
            var folder = await StorageFolder.GetFolderFromPathAsync(sourcePath.Main).AsTask().ConfigureAwait(false);
            var files = await folder.GetFilesAsync().AsTask().ConfigureAwait(false);

            return new TargetFiles();

        }
        public async Task GetFileInfo()
        {

            var dataOperator = DependencyService.Get<IDataOperator>();
            this.SourceAllData = await dataOperator.GetAllSourceDataAsync(this.SourcePath);

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/04/04 01:00

    回答の方に追記しました

    キャンセル

  • 2020/04/04 12:19

    ご回答ありがとうございます
    休日に入ってしまいましたので、週明けにご指摘の箇所を修正してみて
    結果をご報告いたします。
    取り急ぎ、ご連絡まで・・・

    キャンセル

  • 2020/04/06 15:17

    回答ありがとうございました。
    何もかも不慣れで、確認に時間が掛かってしまいましたが
    ご提示頂いた「Resultではなく、awaitを使うように」する方法で
    無事フォルダにアクセス出来ました。
    有無の確認は"FileNotFoundException"の発生有無で確認する様に
    組み立てて行こうと思います。
    又"Task"や"await,async"の非同期処理(?)もコピペで動いている感じなので
    徐々に学習して行きたいと思います。
    長々とお付き合い頂き、ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.21%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる