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

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

新規登録して質問してみよう
ただいま回答率
85.48%
MFC

MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

741閲覧

filesystemで隠しフォルダも取得されてしまう

M9dst7Ks

総合スコア2

MFC

MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2023/12/02 14:53

編集2023/12/03 10:51

実現したいこと

マイドキュメント(Documents)フォルダ配下のフォルダとファイルを取得したいのですが、隠しフォルダ・隠しファイルまで取得されます。

前提

Visual Studio
Visual C++
MFCのアプリケーションを作成しています。
ツリーコントロールにマイドキュメント(Documents)フォルダ配下のフォルダとファイルを表示しようとしています。

マイドキュメント(Documents)フォルダ配下には
「My Music」「My Pictures」「My Videos」「desktop.ini」の隠しファイル・フォルダがあり、以下の設定で表示されるようになります。
エクスプローラのオプション>表示のタブ>
・隠しフォルダ・ファイルを表示するをチェックあり
・保護されたオペレーションシステムファイルを表示しないをチェックなし

発生している問題・エラーメッセージ

隠しフォルダ・隠しファイルである、「My Music」「My Pictures」「My Videos」「desktop.ini」も取得されてしまい、以下のエラーが発生します。

ハンドルされない例外が 0x76659132 (KernelBase.dll) で発生しました(MFCApplication2.exe 内): 0xC000041D: ユーザー コールバック中に未処理の例外が発生しました。。

隠しフォルダ・ファイルを削除・移動する方法以外で、隠しフォルダ・ファイルは取得しないようにする関数等ありましたら、ご教授いただきますと幸いです。

(追記)
ご指摘の通り、再帰呼び出しで落ちていることは確かです。
再帰呼び出しの第2引数が以下になったときにエラーで落ちます。
C:\Users\Public\Documents\My Music

「My Music」「My Pictures」「My Videos」は隠しフォルダであり、ショートカットのため、
これが原因かと推測しておりました…

取得は再帰的に行いたく、マイドキュメントの1階層下のフォルダ(隠しファイルを含まない)を指定したところ、
再帰的にフォルダもファイルも取得することができます。マイドキュメント配下だけうまくいきません。。

(追記2)
ジャンクションと言うのですね。ありがとうございます。
ジャンクションを他のフォルダ内に作成し実行してみたところ、std::filesystemではフォルダとして判別し、
エラーは出ませんでした。

エクスプローラ上で、マイドキュメントの「My Music」「My Pictures」「My Videos」のジャンクションをクリックしたところ、リンク先を開くことはできず反応なしでした。アイコンがグレーがかっています。選択できないことがエラーの原因だと思われます。

該当のソースコード

C++

1 2 // 呼び出し側 3 4 std::filesystem::path p =L"C:\\Users\\Public\\Documents\\"; 5 6 // ルートに設定 7 HTREEITEM hItem = m_tree.InsertItem(p.filename().c_str()); 8 9 recursive(hItem, p); 10 11void CXxxxDlg::recursive(const HTREEITEM &hItem, const std::filesystem::path &p) 12{ 13 for (const std::filesystem::directory_entry &i : std::filesystem::directory_iterator(p)) 14 { 15 if (i.is_directory()) 16 { 17 // フォルダー 18 HTREEITEM hNewItem = m_tree.InsertItem(i.path().filename().c_str(), hItem); 19 20 // 再帰呼び出し 21 recursive(hNewItem, i.path()); 22 } 23 else if (i.is_regular_file()) 24 { 25 // ファイル 26 m_tree.InsertItem(i.path().filename().c_str(), hItem); 27 } 28 } 29} 30

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

ソースコード参考サイト
https://hirokio.jp/programming/cpp-mfc-treeview/

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

lrstatus

2023/12/02 22:23

(1) 問題は隠しディレクトリー・ファイルが見つかることではなく、エラーが発生する(そして、おそらくプロセスが終了してしまうのでは?)ことではないですか? (2) エラーが発生する原因が隠しファイル/ディレクトリーと考えているようですが、列挙されるディレクトリーの中に.(カレントディレクトリー)や..(親ディレクトリー)が含まれていませんか? もし、含まれている場合、この処理では無限に再帰呼び出しが起きてしまい、スタックを使い切ってプロセスが落ちるのではないかと思います。 15行目の前に i の指すパスをデバッグ出力して、取得されるディレクトリーの一覧を確認してはいかがですか。
ikedas

2023/12/03 00:42

エラーメッセージを省略せずに記載してください。このメッセージを公開しても問題はありません。
M9dst7Ks

2023/12/03 01:40

lrstatus様、ikedas様 回答ありがとうございます。追記いたしました。
lrstatus

2023/12/03 06:21

(3) コマンドプロンプトで見てみたところ、隠しではなくジャンクションのようですね。 >C:\Users\Public>dir c:\users\Public\Documents /a > > c:\users\Public\Documents のディレクトリ > >2023/10/05 17:14 <DIR> . >2023/10/05 17:14 <DIR> .. >2019/12/07 18:12 278 desktop.ini >2023/06/29 14:34 <JUNCTION> My Music [C:\Users\Public\Music] >2023/06/29 14:34 <JUNCTION> My Pictures [C:\Users\Public\Pictures] >2023/06/29 14:34 <JUNCTION> My Videos [C:\Users\Public\Videos] もしかすると、std::filesystem では、Windows のジャンクションがうまく扱えないのかもしれませんね。 まったく、別のディレクトリを作って、その上に mklink /j コマンドでジャンクションを作って試せば、ジャンクションの場合かどうは判別できるのではないでしょうか
M9dst7Ks

2023/12/03 10:50

lrstatus様 回答ありがとうございます。追記いたしました。
dameo

2023/12/03 21:28

Visual StudioのバージョンとC++言語のバージョンを記載してください。 以下VS2019、C++17で確認した結果です。 #include <windows.h> #include <shlobj_core.h> #include <filesystem> #include <iostream> namespace fs = std::filesystem; void recursive(const fs::path& path) { std::cout << "recursive(" << path << ")" << std::endl; for (const fs::directory_entry& i : fs::directory_iterator(path)) { if (i.is_directory()) { recursive(i.path()); } else if (i.is_regular_file()) { std::cout << i.path() << std::endl; } } } int main() { std::cout << "_MSC_VER: " << _MSC_VER << std::endl; std::cout << "_MSVC_LANG: " << _MSVC_LANG << std::endl; // カレントディレクトリをホームディレクトリに移動 WCHAR path_home[MAX_PATH]; SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, path_home); SetCurrentDirectory(path_home); // std::filesystemで例外 try { recursive(std::string(".\\Documents\\My Music")); } catch (fs::filesystem_error& e) { std::cerr << e.what() << std::endl; } // 内部(WIN32API)で起きているエラーを再現 WIN32_FIND_DATA result; auto r = FindFirstFileEx(L".\\Documents\\My Music\\*", FindExInfoBasic, &result, FindExSearchNameMatch, nullptr, 0); auto e = GetLastError(); std::cout << r << ", " << e << std::endl; } //_MSC_VER: 1929 //_MSVC_LANG: 201703 //recursive(".\\Documents\\My Music") //directory_iterator::directory_iterator : アクセスが拒否されました。 : ".\Documents\My Music" //FFFFFFFFFFFFFFFF, 5 コード例にあるとおり、Microsoftのstd::filesystem実装はディレクトリ走査にFindFirstFileEx()を使用しています(以下は本日最新のコード)。 https://github.com/microsoft/STL/blob/0403d19f5461fd15983737c3f01ec34800ea9275/stl/src/filesystem.cpp#L245 このFindFirstFileEx()がjunctionで示されたディレクトリ直下の走査でアクセス拒否にて失敗しているのが原因のようです。とりあえずtryで囲めば当該処理をスキップ出来そうな気がします。そもそもジャンクションというシンボリックリンクのようなものをわざわざディレクトリ走査する必要もないと思いますし。一応iteratorが指す要素にはis_symlinkというメソッドが使えますが、VS2019のC++17ではjunctionが含まれず、標準にもドキュメントにもない"_Is_symlink_or_junction"というメソッドを使うことでjunctionをスキップすることができます。 WindowsのUserのホームディレクトリ(?)には怪しげなファイルが大量にあると思うので、自分で調べながら地道にコツコツ進めるのでなければ、(バグの温床になるので)ここをフルに走査すること自体をオススメしません。
lrstatus

2023/12/04 12:17

(4) なるほど、dameoさんの調査によるとアクセス拒否が起きているのですね。 そこで、エクスプローラーで当該ジャンクションのプロパティのセキュリティタブで権限を確認したところ、見事なほど権限が設定されてないですね。 つまりジャンクションの先ではなく、ジャンクションそのものがほぼアクセス禁止状態です。 かつ、エクスプローラーの「保護されたオペレーションシステムファイルを表示しない」にチェックを入れると当該ジャンクションは表示されないことから、Windowsシステム側からは、「このジャンクションの中は探ってくれるな」というシロモノかもしれません。 アクセス権限が与えられていない以上、その中を探って表示させるのは無理だと思います。
lrstatus

2023/12/04 12:53

訂正:ほとんど権限が与えられていない訳ではなく、FILE_READ_DATAにDENY(拒否)がついているようです。"My Music"はDENYがついていますが、おなじ並びにあるdesktop.iniはDENYがついていません。 C:\Users\user\Documents>dir /a ドライブ C のボリューム ラベルがありません。 ボリューム シリアル番号は 4401-80D1 です C:\Users\user\Documents のディレクトリ 2023/10/05 17:14 <DIR> . 2023/10/05 17:14 <DIR> .. 2023/06/29 15:01 402 desktop.ini 2023/06/29 14:58 <JUNCTION> My Music [C:\Users\user\Music] 2023/06/29 14:58 <JUNCTION> My Pictures [C:\Users\user\Pictures] 2023/06/29 14:58 <JUNCTION> My Videos [C:\Users\user\Videos] 1 個のファイル 402 バイト 6 個のディレクトリ 274,800,689,152 バイトの空き領域 C:\Users\user\Documents>cacls "My Music" C:\Users\user\Documents\My Music Everyone:(DENY)(特殊なアクセス:) FILE_READ_DATA NT AUTHORITY\SYSTEM:(OI)(CI)(ID)F BUILTIN\Administrators:(OI)(CI)(ID)F DESKTOP-GFI4A6U\user:(OI)(CI)(ID)F C:\Users\user\Documents>cacls "desktop.ini" C:\Users\user\Documents\desktop.ini NT AUTHORITY\SYSTEM:F BUILTIN\Administrators:F DESKTOP-GFI4A6U\user:F
dameo

2023/12/04 19:14

あなたのUserは恐らくDESKTOP-GFI4A6U\userなので、F、つまりFull Accessです。 継承している権限もありますが、Fがあれば関係ないと思います。
M9dst7Ks

2023/12/08 12:55

皆様 ご回答いただきありがとうございます。 「My Music」「My Pictures」「My Videos」のジャンクションはアクセス権がないとのことなので、アクセス権があるかどうか、try,cahthで試してから、filesystemのiteratorを実行しようかと考えております。
dameo

2023/12/08 14:35 編集

cacls/icaclsなどによるとアクセス権がないのはEveryoneという匿名ユーザー(認証してない状態)の場合なので、もしあなたがDESKTOP-GFI4A6U\userならアクセス権がないうわけではありません。 例えば dir "C:\Users\user\Documents\My Music"などとすればエラーにはならないはずです。 私が調べたのはVS2019のC++標準ライブラリの中のコードで、FindFirstFileEx()の処理結果です。 これが5(ERROR_ACCESS_DENIED)を返しており、そのためにC++の例外が発生しています。 APIの解説を読むと https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-findfirstfileexa 「パスがシンボリック リンクを指している場合、 WIN32_FIND_DATA バッファーには、ターゲットではなくシンボリック リンクに関する情報が含まれます。」 とあるので、junctionは中身まで辿らないのではないかと予想しています(未確認)。 そのため引数で"C:\Users\user\Documents\My Music\*"のような指定をされた場合にアクセス拒否なのではないかと考えています(未確認)。 全ての挙動を確認して初めて対応を考えることが出来るのではないかと思いますよ。 あと > Visual StudioのバージョンとC++言語のバージョンを記載してください。 また繰り返しになりますが、 > WindowsのUserのホームディレクトリ(?)には怪しげなファイルが大量にあると思うので、自分で調べながら地道にコツコツ進めるのでなければ、(バグの温床になるので)ここをフルに走査すること自体をオススメしません。 とてもじゃないけど地道にコツコツやってるように見えないので、他の方法を考えた方がいいと思いますよ。
guest

回答2

0

ベストアンサー

「My Music」「My Pictures」「My Videos」を読み飛ばす方法です。

Windows 11/10、Visual Studio 2022、C++17にて確認

for (const std::filesystem::directory_entry &i : std::filesystem::directory_iterator(p)) { // 以下の4行を追加 if (i.symlink_status().type() == std::filesystem::file_type::junction) { continue; }

std::filesystem::file_type

symlink_statusstatus
My Musicjunctiondirectory
My Picturesjunctiondirectory
My Videosjunctiondirectory
desktop.iniregularregular

Visual Studio 2022のfilesystemのソースでは、以下のように定義されています。

_EXPORT_STD enum class file_type { none, not_found, regular, directory, symlink, block, // not used on Windows character, // not used in this implementation; theoretically some special files like CON // might qualify, but querying for this is extremely expensive and unlikely // to be useful in practice fifo, // not used on Windows (\\.\pipe named pipes don't behave exactly like POSIX fifos) socket, // not used on Windows unknown, junction // implementation-defined value indicating an NT junction };

is_directoryはstatus、is_symlinkはsymlink_statusより判定しているようですが、symlinkとjunctionは別物として定義されています。

投稿2023/12/10 07:59

hiroki-o

総合スコア485

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

リンク先のブログ作者です。

lrstatusさん

列挙されるディレクトリーの中に.(カレントディレクトリー)や..(親ディレクトリー)が含まれていませんか?もし、含まれている場合、この処理では無限に再帰呼び出しが起きてしまい、スタックを使い切ってプロセスが落ちるのではないかと思います。

そうならないことを確認したうえで、ブログに掲載しています。

M9dst7Ksさん
下記のソースで、どうでしょうか?

if (i.is_directory()) { // フォルダー HTREEITEM hNewItem = m_tree.InsertItem(i.path().filename().c_str(), hItem); // 再帰呼び出し if (i.path() == L"C:\\Users\\Public\\Documents\\My Music") { recursive(hNewItem, L"C:\\Users\\Public\\Music"); } else if (i.path() == L"C:\\Users\\Public\\Documents\\My Pictures") { recursive(hNewItem, L"C:\\Users\\Public\\Pictures"); } else if (i.path() == L"C:\\Users\\Public\\Documents\\My Videos") { recursive(hNewItem, L"C:\\Users\\Public\\Videos"); } else { recursive(hNewItem, i.path()); } }

もし、パスをベタ書きしたくないとか、汎用性を持たせたいなら、SHGetKnownFolderPath APIにて「My Music」「My Pictures」「My Videos」の実体のパスを取得できます。

https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath

1番目の引数

自ユーザーPublic
ドキュメントFOLDERID_DocumentsFOLDERID_PublicDocuments
My MusicFOLDERID_MusicFOLDERID_PublicMusic
My PicturesFOLDERID_PicturesFOLDERID_PublicPictures
My VideosFOLDERID_VideosFOLDERID_PublicVideos

2番目の引数はゼロ、3番目の引数はnullptr、4番目の引数はwchar_tのポインターを指定します。4番目の引数は使用後にCoTaskMemFreeにて解放することをお忘れなく。

(Windows 11、Visual Studio 2022、C++17にて確認)

投稿2023/12/08 05:45

hiroki-o

総合スコア485

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

M9dst7Ks

2023/12/08 13:06

リンク先のブログの作者様 記事を参考にさせていただいております。こちらにご回答までいただきありがとうございます。 アクセス権のないジャンクションファイルが私のPC環境に存在するため、上記13行目、 for (const std::filesystem::directory_entry &i : std::filesystem::directory_iterator(p)) でアクセス違反でエラーが発生しておりました。 アクセス権をtry・catchで確認してからiteratorを実行できればよいなと考えております。
hiroki-o

2023/12/08 13:42 編集

M9dst7Ksさんの環境では、「My Music」「My Pictures」「My Videos」各フォルダー以外にもジャンクションが存在するのですか? こちらの確認結果(上記はWindows 11ですが、Windows 10でも試しました)では、ジャンクション自体はi.path().filename().c_str()が取れるが、その配下にアクセスができないので、上記のように実体のフォルダーに振り替えています。 実体のフォルダーは、エクスプローラーでアクセス可能でしょうか? (上記の回答は、質問のようにマイドキュメントから検索することを前提としています。「My Music」「My Pictures」「My Videos」から検索する場合は、すでにジャンクションとわかっているので、SHGetKnownFolderPath APIを使って本当のフォルダーにアクセスしてください)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問