1

I am trying to recursively get all of the files — 5 or 6 files give or take — under the directory C:\YC. I am bound to one WMI call to a remote computer.

I managed to do this call using the WQL LIKE operator but it's taking around 30 seconds, even though the result is around 6 files:

// USING A WQL QUERY
string query = "SELECT Name,LastModified FROM CIM_DataFile WHERE PATH LIKE '\\\\YC\\\\%' AND DRIVE ='C:'";
ObjectQuery oQuery = new ObjectQuery();
oQuery.QueryString = query;

ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(scope, oQuery);
oSearcher.Options.Rewindable = false;

// Takes long time
ManagementObjectCollection oReturnCollection = oSearcher.Get();

// SHOWING EACH FILE
foreach (ManagementObject oReturn in oReturnCollection)
{
     Console.WriteLine(oReturn["Name"]?.ToString());
}

Is there a more efficient way using System.Management objects to get the files recursively (with or without WQL, but with one WMI call)?

Lance U. Matthews
  • 15,725
  • 6
  • 48
  • 68
Yiftach Cohen
  • 39
  • 1
  • 4

1 Answers1

0

It seems that using WMI to query the filesystem is very slow when using the LIKE operator to filter on the Name/Path property, even if the filter value doesn't contain a wildcard (%).

This will take more than one query, but you can get the CIM_Directory (actually Win32_Directory) instance for your base directory and traverse the hierarchy yourself...

static void EnumerateDirectory(ManagementObject directory, bool recurse)
{
    // Get all related CIM_LogicalFile instances where this is the parent (GroupComponent)
    // and the related instance is the child (PartComponent)
    using (ManagementObjectCollection children = directory.GetRelated("CIM_LogicalFile", null, null, null, "PartComponent", "GroupComponent", false, null))
        foreach (ManagementObject child in children.Cast<ManagementObject>())
            switch (child.Path.ClassName)
            {
                case "CIM_Directory":
                case "Win32_Directory":
                    Console.WriteLine($"Directory: { child["Name"] }");
                    if (recurse)
                        EnumerateDirectory(child, true);
                    break;
                case "CIM_DataFile":
                    Console.WriteLine($"     File: { child["Name"] }");
                    break;
                default:
                    Console.WriteLine($"ERROR: Unexpected { child.Path.ClassName } instance.  This should never happen!");
                    break;
            }
}

That's calling GetRelated() to get all CIM_LogicalFile instances associated with directory; CIM_LogicalFile is the parent class of CIM_DataFile and CIM_Directory, which itself is the parent of Win32_Directory. We could call the simpler directory.GetRelated("CIM_LogicalFile") overload but that would return one CIM_Directory instance we don't want: the parent directory. Instead, we call that longer overload that allows us to specify that we want directory to be the parent in the CIM_DirectoryCIM_Directory relationship.

You would call EnumerateDirectory() like this...

static void Main()
{
    const string directoryPath = @"C:\YC";
    ObjectQuery directoryQuery = new SelectQuery(
        "CIM_Directory",
        $"Name = \"{ directoryPath.Replace(@"\", @"\\") }\""
    );

    using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(directoryQuery))
    using (ManagementObjectCollection results = searcher.Get())
    using (ManagementObject directory = results.Cast<ManagementObject>().SingleOrDefault())
    {
        if (directory == null)
            Console.WriteLine($"ERROR: The query ' { directoryQuery.QueryString } ' returned no results.");
        else
            EnumerateDirectory(directory, true);
    }
}

If you only want the immediate child files in a directory that code is much simpler...

static void EnumerateFiles(ManagementObject directory)
{
    using (ManagementObjectCollection children = directory.GetRelated("CIM_DataFile"))
        foreach (ManagementObject child in children.Cast<ManagementObject>())
            Console.WriteLine($"File: { child["Name"] }");
}
Lance U. Matthews
  • 15,725
  • 6
  • 48
  • 68