Using IPlatformFile::IterateDirectory

Hi Guys

First off, thank you for an excellent product. Then, to my problem; I need to programmatically obtain all available drives in the OS and to acquire a listing of the child items from a given directory.

I believe I’ve made a reasonable effort to search for some kind of tutorial but haven’t managed to find anything useful. Just when I thought I’d find something, like Rama’s excellent tutorials (someone give that man a medal already), the carpet gets pulled out from under me (GFileManager being discontinued).

I wanted to revert to the WinAPI but Rama’s comment “I have read that using System() is not such a good idea” made me hesitant: https://answers.unrealengine.com/questions/2711/how-to-open-os-file-browser-while-in-game-and-util.html
Engine/Source/Runtime/Core/Public/Misc/Paths.h is unfortunately not the solution I need.

Basically I’m trying to create an in-game file explorer with some basic file management functionality (copy, move, delete, etc.) but I can’t even list items in a directory or available drives (like Rama’s Slate Tree View here A new, community-hosted Unreal Engine Wiki - Announcements - Unreal Engine Forums). I tried using IPlatformFile::IterateDirectory or IPlatformFile::IterateDirectoryRecursively and their base- and sub classes but don’t know how to use IPlatformFile::FDirectoryVisitor.

Might I ask for just some basic code to get started with, please? Any explanation of which parts do what would be welcomed as well, but I’ll definitely by happy with just some code that can show me how to get all available drives and all child items in a folder/directory. A platform-neutral solution would also be awesome, but a Windows-only one would be welcome in itself. Alternatively, if you could even point me in the direction of a tutorial, that would be even better.

If you don’t mind me pushing my luck, please add a succint explanation on why it would be a bad idea to use the OS’s API for file management… or am I mistaken in that understanding?

Full disclosure: I’m self-taught and have, in the last year, changed from VB .NET to C#. I’ve started with some C++ tutorials especially for UE4 but I make no claim to being a C++ guru… yet. Which gives me the feeling that I’m setting myself up for an epic facepalm moment right now… but what the hell, as long as we’re learning, right?

Thanking you in advance,
KatoNamus

I am wishing you luck with this, would like to see what Epic has to say about iterating over drive letters, I am not sure how to do that :slight_smile:

I only know how to iterate within a specified drive letter.

Hey Rama! So glad you’re here :smiley:
Would you mind sharing how to iterate through a folder, please? I’ve been searching some more and if it’s not a problem to go WinAPI then this might be a per-drive solution: c++ - Enumerating all available drive letters in Windows - Stack Overflow but it’s obviously not OS-independent.

I’m sure someone’s going to advise me to do something different, but for now I’m trying to use the WinAPI to accomplish my goals. I found some good information at Displaying Volume Paths - Win32 apps | Microsoft Docs and hacked something out of that.

MyTestObject.h

#include "Object.h"
#include "MyTestObject.generated.h"

UCLASS(NotPlaceable)
class UMyTestObject : public UObject
{
	GENERATED_UCLASS_BODY()

	/* Returns an array of strings with available physical drive paths, i.e. A:\ or C:\
	No networked/mapped drives */
	UFUNCTION(BlueprintPure, meta = (FriendlyName = "Get Drive Paths", Keywords = "Array String Drive Paths"), Category = "MyNodes|FileManagement")
	static TArray<FString> GetDrivePaths();
};

…and MyTestObject.cpp

#include "MyTestObject.h"

UMyTestObject::UMyTestObject(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
}

TArray<FString> UMyTestObject::GetDrivePaths()
{
	TArray<FString> DrivePaths;
	uint32 CharCount = 0;
	WCHAR DeviceName[MAX_PATH] = L"";
	uint32 Error = ERROR_SUCCESS;
	HANDLE FindHandle = INVALID_HANDLE_VALUE;
	BOOL Found = false;
	size_t Index = 0;
	BOOL Success = false;
	WCHAR VolumeName[MAX_PATH] = TEXT("");

	// Enumerate all volumes in the system
	FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName));

	if (FindHandle == INVALID_HANDLE_VALUE)
	{
		Error = GetLastError();
		// wprintf(L"FindFirstVolumeW failed with error code %d", Error);
		DrivePaths.Empty();
		return DrivePaths;
	}

	do 
	{
		// Skip the \\?\\ prefix and remove the trailing backslash
		Index = wcslen(VolumeName) - 1;

(to be continued)

if (VolumeName[0] != L’\’ ||
VolumeName[1] != L’\’ ||
VolumeName[2] != L’?’ ||
VolumeName[3] != L’\’ ||
VolumeName[Index] != L’\’)
{
Error = ERROR_BAD_PATHNAME;
// wprintf(L"FindFirsVolumeW/FindNextVolumeW returned a bad path: %s\n", VolumeName);
break;
}

		// QueryDosDeviceW does not allow a trailing backslash, so temporarily remove it.
		VolumeName[Index] = L'\0';

		CharCount = QueryDosDeviceW(&VolumeName[4], DeviceName, ARRAYSIZE(DeviceName));

		VolumeName[Index] = L'\\';

		if (CharCount == 0)
		{
			Error = GetLastError();
			// wprintf(L"QueryDosDeviceW failed with error code %d\n", Error);
			break;
		}

		/*
		wprintf(L"\nFound a device: %s", DeviceName);
		wprintf(L"\nVolume name: %s", VolumeName);
		wprintf(L"\nPaths: ");
		*/

		uint32 chCount = MAX_PATH + 1;
		PWCHAR Names = NULL;
		PWCHAR NameIdx = NULL;
		BOOL bSuccess = false;

		for (;;)
		{
			// Allocate a buffer to hold the paths
			Names = (PWCHAR) new BYTE[chCount * sizeof(WCHAR)];

			if (!Names) break; // If memory can't be allocated, break

			// Obtain all of the paths for this volume
			bSuccess = GetVolumePathNamesForVolumeNameW(VolumeName,
				Names,
				chCount,
				(PDWORD)&chCount);

			if (bSuccess) break;

			if (GetLastError() != ERROR_MORE_DATA) break;

			delete[] Names;
			Names = NULL;
		}

		if (bSuccess)
		{
			// "Display" the various paths
			for (NameIdx = Names;
				NameIdx[0] != L'\0';
				NameIdx += wcslen(NameIdx)+1)
			{
				DrivePaths.Add(NameIdx);
			}
		}

		if (Names != NULL)
		{
			delete[] Names;
			Names = NULL;
		}

(to be continued)

// Move on to the next Volume
Success = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName));

		if (!Success)
		{
			Error = GetLastError();
			if (Error != ERROR_NO_MORE_FILES)
			{
				// wprintf(L"FindNextVolumeW failed with error code %d\n", Error);
				break;
			}

			// Finished iterating through all the volumes
			Error = ERROR_SUCCESS;
			break;
		}

	} while (Error != ERROR_NO_MORE_FILES);
	
	FindVolumeClose(FindHandle);
	FindHandle = INVALID_HANDLE_VALUE;

	DrivePaths.Sort();
	return DrivePaths;
}

Hideous, I know. But it works… for now. And it’s exposed to blueprints.

oh wow!

Very impressive that you figured this all out!

Congratulations!

#:heart:

Rama

Haha, thanks Rama. Coming from you, that’s a helluva compliment (I’m referring of course to the skills you’ve already demonstrated so far surpassing my own by leaps and bounds). :smiley:

I’ve also added a funtion to get child items from a given folder path (Listing the Files in a Directory - Win32 apps | Microsoft Docs) and extrapolated on that to check if a given path is a folder or not.

If anyone is interested I can paste that source when I’m home again.

GetFilesAndDirectories

GetFiles

GetDirectories

I had offered these functions to Epic using their own system but they did not accept it

I use my own version of these functions all the time, seems pretty essential to me :slight_smile:

You can see the code I offered to Epic here:

GitHub Link

https://github.com/EpicGames/UnrealEngine/pull/23/files

You could add this to your version of the Engine to get this functionality in a Multi-platform compatible way

Or, you can do what I do, and copy out the FunctorFileDirectoryVisitor as a separate header file that you include in your own code, and then copy the functions into a static function library for use anywhere in your CPP code base.

I am still very impressed by all that you achieved!

PS: if you or others have sufficient interest in this I could try resubmitting the pull request with new and improved code and see what they say

I entirely rely on my own version of GetFiles and GetDirectories

:slight_smile:

You can see the code I offered to Epic here:

Oohh… very nice. I’ll be taking a closer look at that as soon as possible, thanks.

Or, you can do what I do, and copy out the FunctorFileDirectoryVisitor as a separate header file that you include in your own code, and then copy the functions into a static function library for use anywhere in your CPP code base.

That’s most likely what I’ll be doing. So far I’ve just been toying with “scratchpad” projects (my coding version of a paper napkin design) to achieve specific desired effects. But now that I’ve got one of those requirements for my learning project, I should probably start a personal code library.

I am still very impressed by all that you achieved!

Hehe, thanks man. Reading that makes me sit down for another Unreal session instead of watching a movie/episode simple entertainment… :stuck_out_tongue:

PS: if you or others have sufficient interest in this I could try resubmitting the pull request with new and improved code and see what they say
I entirely rely on my own version of GetFiles and GetDirectories

I’d support that, but then again, I’m biased - my planned project is all about file management.