nicad49
nicad49

Reputation: 23

NetFileEnum returns ERROR_MORE_DATA

Attempting to retrieve a large list of open files from a file server returns the ERROR_MORE_DATA value (Error number 234), but works fine when dealing with only a small list of files (seems to return around 84 entries). This code is based upon the example at: http://pinvoke.net/default.aspx/netapi32/NetFileEnum.html

Most examples I have found don't really touch on how to handle a large number of files. From my understanding, this has something to do with the resume_handle, however I'm not sure what needs to be done. Do I need to somehow call this method in a loop?

Code is as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace OpenFiles
{
    class Program
    {
        public static string computername = "computername";
        static void Main(string[] args)
        {

            List<string> theFileList = NativeMethods.GetFiles(computername);
            foreach (string file in theFileList)
            {
                Console.WriteLine(file);
            }
        }
    }

    static class NativeMethods
    {

        [DllImport("netapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern int NetFileEnum(
            string servername,
            string basepath,
            string username,
            int level,
            ref IntPtr bufptr,
            int prefmaxlen,
            out int entriesread,
            out int totalentries,
            IntPtr resume_handle
        );

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        struct FILE_INFO_3
        {
            public int fi3_id;
            public int fi3_permission;
            public int fi3_num_locks;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string fi3_pathname;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string fi3_username;
        }

        [DllImport("Netapi32.dll", SetLastError = true)]
        static extern int NetApiBufferFree(IntPtr Buffer);

        public static List<string> GetFiles(string Computername)
        {
            const int MAX_PREFERRED_LENGTH = -1;

            int dwReadEntries;
            int dwTotalEntries;
            IntPtr pBuffer = IntPtr.Zero;
            FILE_INFO_3 pCurrent = new FILE_INFO_3();
            List<string> fileList = new List<string>();

            int dwStatus = NetFileEnum(Computername, null, null, 3, ref pBuffer, MAX_PREFERRED_LENGTH, out dwReadEntries, out dwTotalEntries, IntPtr.Zero);

            if (dwStatus == 0)
            {

                for (int dwIndex = 0; dwIndex < dwReadEntries; dwIndex++)
                {

                    IntPtr iPtr = new IntPtr(pBuffer.ToInt32() + (dwIndex * Marshal.SizeOf(pCurrent)));
                    pCurrent = (FILE_INFO_3)Marshal.PtrToStructure(iPtr, typeof(FILE_INFO_3));

                    string fileInfo = pCurrent.fi3_id + "," + 
                        pCurrent.fi3_num_locks + "," + 
                        pCurrent.fi3_pathname + "," + 
                        pCurrent.fi3_permission + "," + 
                        pCurrent.fi3_username;

                    fileList.Add(fileInfo);

                }

                NetApiBufferFree(pBuffer);

            }
            else
            {
                Console.WriteLine("error: " + dwStatus);
            }
            return fileList;
        }
    }
}

Upvotes: 1

Views: 851

Answers (1)

Ryan Brown
Ryan Brown

Reputation: 66

In my limited experience a very large number of results can be larger than the max buffer. In this case a more data response is given and instructs us to call again with the provided resume handle. In your example, the resume handle will not be changed because the DllImport signature doesn't define it as an out parameter. Using the resume handle result from the first call (passing in zero means first call) allows you to receive the next batch. Loop until you receive a success response or some other error.

Be sure to fix the issue where the NetFileEnum signature is defined. The resume handle is not defined with an out so cannot be changed by the called function.

Try the following DLL Import signature instead:

[DllImport("netapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern int NetFileEnum(
    string servername,
    string basepath,
    string username,
    int level,
    ref IntPtr bufptr,
    int prefmaxlen,
    out int entriesread,
    out int totalentries,
    out IntPtr resume_handle
);

You should be able to re-call NetFileEnum with the resulting resume handle multiple times when you get a more data response.

Upvotes: 1

Related Questions