找出用户名(谁)在C#中修改文件

我正在使用FileSystemWatcher来监视一个文件夹。 但是当目录中发生一些事件时,我不知道如何search谁对这个文件产生影响。 我试图使用EventLog。 它只是不能工作。 还有另一种方法吗?

我不记得我在哪里find这个代码,但它是使用pInvoke的替代方法,我认为这个任务有点矫枉过正。 使用FileSystemWatcher观察文件夹,当事件触发时,可以使用以下代码计算出哪个用户更改了文件:

 private string GetSpecificFileProperties(string file, params int[] indexes) { string fileName = Path.GetFileName(file); string folderName = Path.GetDirectoryName(file); Shell32.Shell shell = new Shell32.Shell(); Shell32.Folder objFolder; objFolder = shell.NameSpace(folderName); StringBuilder sb = new StringBuilder(); foreach (Shell32.FolderItem2 item in objFolder.Items()) { if (fileName == item.Name) { for (int i = 0; i < indexes.Length; i++) { sb.Append(objFolder.GetDetailsOf(item, indexes[i]) + ","); } break; } } string result = sb.ToString().Trim(); //Protection for no results causing an exception on the `SubString` method if (result.Length == 0) { return string.Empty; } return result.Substring(0, result.Length - 1); } 

Shell32是对DLL的引用:Microsoft Shell Controls And Automation – 它是一个COM参考

以下是您如何调用方法的一些示例:

 string Type = GetSpecificFileProperties(filePath, 2); string ObjectKind = GetSpecificFileProperties(filePath, 11); DateTime CreatedDate = Convert.ToDateTime(GetSpecificFileProperties(filePath, 4)); DateTime LastModifiedDate = Convert.ToDateTime(GetSpecificFileProperties(filePath, 3)); DateTime LastAccessDate = Convert.ToDateTime(GetSpecificFileProperties(filePath, 5)); string LastUser = GetSpecificFileProperties(filePath, 10); string ComputerName = GetSpecificFileProperties(filePath, 53); string FileSize = GetSpecificFileProperties(filePath, 1); 

您需要在文件系统上启用审核(审核仅在NTFS上可用)。 您可以通过应用组策略或本地安全策略来执行此操作。 您还必须对要监视的文件启用审计。 您可以像修改文件权限一样进行操作。

然后将审核事件写入安全事件日志。 您将不得不监视这个事件日志,以查看您感兴趣的审计事件。一种方法是创build计划任务,在您感兴趣的事件被logging时启动应用程序。 如果事件没有以很高的速度logging下来,为每个事件启动一个新的过程是唯一可行的。 否则,您可能会遇到性能问题。

基本上,你不想看文件的内容或属性(shell函数GetFileDetails所做的)。 此外,您不希望使用文件共享API来获取打开文件的networking用户( NetGetFileInfo所做的)。 你想知道上次修改文件的进程的用户。 Windows通常不会logging此信息,因为这需要太多的资源来处理所有文件活动。 相反,您可以select性地启用对特定用户进行审计,对特定文件(和文件夹)执行特定操作。

看来你需要调用Windows API函数来获得你想要的,这涉及PInvoke。 另一个论坛上的一些人一直在研究它,并找出一些东西 ,你可以在这里find他们的解决scheme 。 但是,它似乎只适用于networking共享上的文件(不在本地计算机上)。

为了将来的参考,这是由dave4dl发布的代码 :

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

这已经被讨论了很多次。 我从同一个问题的答复:

您不能与FileSystemWatcherasynchronous执行此操作,但可以使用文件系统filter驱动程序同步执行此操作。 该驱动程序让您获取执行操作的帐户的用户名。

使用dave4dl发布的代码并更新声明struct FILE_INFO_3,如下所示,可以监控用户名的创build和更新文件操作(就像FileSystemWatcher和OpenFiles.exe的function组合FileSharing Server的function)

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public 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; }