Link to home
Start Free TrialLog in
Avatar of dave4dl
dave4dl

asked on

Record Each Time A File Is Created

I have a fileserver on windows 2008 and I would like to record (in a text file) each time a file is created on the fileserver drive and who created it.  I know it is possible (because tools like procmon can monitor all file creation events) but I am not sure how to do it.  Does anyone know how to catch that event (programmatically)?

I writing this program in C# (so if you could give me code snippets, urls, or web pages that use a .NET language, I would appreciate it).

Thanks,

David
Avatar of r3nder
r3nder
Flag of United States of America image

ASKER CERTIFIED SOLUTION
Avatar of r3nder
r3nder
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Check this code It May be Useful :
public class Watcher
{

    public static void Main()
    {

        string[] args = System.Environment.GetCommandLineArgs();
 
        // If a directory is not specified, exit program.
        if(args.Length != 2)
        {
            // Display the proper way to call the program.
            Console.WriteLine("Usage: Watcher.exe (directory)");
            return;
        }

        // Create a new FileSystemWatcher and set its properties.
        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = args[1];
        /* Watch for changes in LastAccess and LastWrite times, and 
           the renaming of files or directories. */
        watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite 
           | NotifyFilters.FileName | NotifyFilters.DirectoryName;
        // Only watch text files.
        watcher.Filter = "*.txt";

        // Add event handlers.
        watcher.Changed += new FileSystemEventHandler(OnChanged);
        watcher.Created += new FileSystemEventHandler(OnChanged);
        watcher.Deleted += new FileSystemEventHandler(OnChanged);
        watcher.Renamed += new RenamedEventHandler(OnRenamed);

        // Begin watching.
        watcher.EnableRaisingEvents = true;

        // Wait for the user to quit the program.
        Console.WriteLine("Press \'q\' to quit the sample.");
        while(Console.Read()!='q');
    }

    // Define the event handlers.
    private static void OnChanged(object source, FileSystemEventArgs e)
    {
        // Specify what is done when a file is changed, created, or deleted.
       Console.WriteLine("File: " +  e.FullPath + " " + e.ChangeType);
    }

    private static void OnRenamed(object source, RenamedEventArgs e)
    {
        // Specify what is done when a file is renamed.
        Console.WriteLine("File: {0} renamed to {1}", e.OldFullPath, e.FullPath);
    }
}

Open in new window

This link might also help you a lot..A good example of asynchronous file monitoring:
http://www.dreamincode.net/forums/topic/115653-using-the-filesystemwatcher-class/
Avatar of dave4dl
dave4dl

ASKER

Fantastic, thank you both very much, I was not aware of the FileSystemWatcher class before and it looks very useful.

The one thing I can't seem to figure out though is how to determine which user made the change.  Do either of you know how to do that?
Yes u can just use this to fetch the current user :
System.Environment.UserName.ToString();

Open in new window

If u found this solution helpful plz accept the solution
many are quite useful my personal favorite is the background worker
Avatar of dave4dl

ASKER

sorry, system.environment.username just returns the name of the user running the monitoring app.  I was looking for the user that created the file on the fileserver.
Avatar of dave4dl

ASKER

On this site someone says they did it using code snippets from another site: http://vbcity.com/forums/t/133307.aspx?PageIndex=2 but doesnt say exactly which snippets (and I am not sure how to determine which ones will do this job).

The site that the snippets came from was http://www.pinvoke.net/
http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=8164
This is an entire app for a filewatcher
in there they use
userName = System.Environment.UserName;
with logfile
Click on filesyswatcher.cs - or just download it all :)
Avatar of dave4dl

ASKER

Thank you both for your help on this.

The Environment.UserName is the username currently logged in which isnt what I am looking for, I was actually trying to get the username of the person working with files our our fileserver (over the network).

I did a little exploration of some functions listed on the pinvoke website and found the combinations of windows api functions to do this.  I wrote a couple functions (in C#) to accomplish this using those api methods and posted that code with this comment.  For this code below to work, you have to import the following namespaces:

using System.Net;
using System.IO;
using System.Security.Principal;
using System.Runtime.InteropServices;

In addition, this code will just show you the username of the person accessing the file over the network (so it won't work on localhost if you are creating the file from localhost).
[DllImport("Netapi32.dll", SetLastError = true)]
        static extern int NetApiBufferFree(IntPtr Buffer);

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

        [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
        );

        [DllImport("netapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern int NetFileGetInfo(
          string servername,
          int fileid,
          int level,
          ref IntPtr bufptr
        );

        private int GetFileIdFromPath(string filePath)
        {
            const int MAX_PREFERRED_LENGTH = -1;

            int dwReadEntries;
            int dwTotalEntries;
            IntPtr pBuffer = IntPtr.Zero;
            FILE_INFO_3 pCurrent = new FILE_INFO_3();

            int dwStatus = NetFileEnum(null, filePath, 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));

                    int fileId = pCurrent.fi3_id;

                    //because of the path filter in the NetFileEnum function call, the first (and hopefully only) entry should be the correct one
                    NetApiBufferFree(pBuffer);
                    return fileId;
                }
            }

            NetApiBufferFree(pBuffer);
            return -1;  //should probably do something else here like throw an error
        }


        private string GetUsernameHandlingFile(int fileId)
        {
            string defaultValue = "[Unknown User]";

            if (fileId == -1)
            {
                return defaultValue;
            }

            IntPtr pBuffer_Info = IntPtr.Zero;
            int dwStatus_Info = NetFileGetInfo(null, fileId, 3, ref pBuffer_Info);

            if (dwStatus_Info == 0)
            {
                IntPtr iPtr_Info = new IntPtr(pBuffer_Info.ToInt32());
                FILE_INFO_3 pCurrent_Info = (FILE_INFO_3)Marshal.PtrToStructure(iPtr_Info, typeof(FILE_INFO_3));
                NetApiBufferFree(pBuffer_Info);
                return pCurrent_Info.fi3_username;
            }
            
            NetApiBufferFree(pBuffer_Info);
            return defaultValue;  //default if not successfull above
        }

        private string GetUsernameHandlingFile(string filePath)
        {
            int fileId = GetFileIdFromPath(filePath);
            return GetUsernameHandlingFile(fileId);
        }

/////////////////////////////////////////////////////////////


        private void fileSystemWatcher1_Created(object sender, System.IO.FileSystemEventArgs e)
        {
            bool isTempFile = e.Name.StartsWith("~$") || e.Name.EndsWith(".tmp");

            if (isTempFile == false)
            {
                LogEvent(
                    string.Format   ("{0} was just created by {2} (owned by {4})", 
                                    e.FullPath, 
                                    e.ChangeType,
                                    GetUsernameHandlingFile(GetFileIdFromPath(e.FullPath)), 
                                    IpArrayToString(Dns.GetHostAddresses(Environment.MachineName)),
                                    new FileInfo(e.FullPath).GetAccessControl().GetOwner(typeof(NTAccount))
                                    )
                    );
            }
        }

Open in new window

I had given a valid solution so I too deserve points..
Avatar of dave4dl

ASKER

starlite551,

I'm sorry but I dont think you contributed enough beyond what r3nder did (30 minutes before your post) to take any points away from that solution.  Your follow up post about System.Environment.UserName.ToString() also missed the mark (see my last post for the actual solution).  

To be honest, as someone who answers questions for points, it bothers me a little bit when other people come alone and repost the same solution I post and expect to take some of the points that rightfully should go to me.  I think the original question asker (and point assigner) usually feels like it is equitable to spread the points around to everyone who helped but they dont think about how unfair it is to the person who actually came up with the solution first.

I really do appreciate your help and I think people with other different (but similar) problems will find your posts helpful but as far as points go, r3nder beat you to the punch this time.

I hope you have no hard feelings about this.
My answer was a valid one still he ignored it and gave all points to r3nder
Avatar of dave4dl

ASKER

Sure your answer is valid but r3nder posted it first.
I must get assisted solution atleast.. I accept that he gave u first solution but u can always give an appreciation for some1 who tried helping u out..