2012-07-25 49 views
5

我正在使用FileSystemWatcher監視文件夾。但是,當目錄中發生某些事件時,我不知道如何搜索誰對該文件產生影響。我試圖使用EventLog。它只是無法工作。還有另一種方法可以做到嗎?找出用戶名(誰)在C#中的修改文件

+0

可能重複[使用FileSystemWatcher類記錄是誰在做更改的用戶(http://stackoverflow.com/questions/8649661/use-the-filesystemwatcher -class-to-document-the-user-who-making-changes) – 2012-07-26 04:41:26

回答

7

我不記得在那裏我發現這個代碼,但它使用的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的引用:微軟殼牌控制和自動化- 它是一個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); 
+0

據我所知,「GetDetailsOf」的索引10獲取文檔的標題,而不是「最後一個用戶」。 – 2012-07-26 01:12:44

+0

@Jeremy Thompson我該如何使用Shell32.shell?似乎它不是一個參考問題。導入的東西沒有幫助 – 2012-07-26 17:42:45

+0

'Shell32.shell'可通過** Microsoft Shell Controls And Automation **獲得 - 它是一個COM引用。 – 2012-10-23 21:34:36

4

您需要啓用對文件系統的審計(並且審計僅在NTFS上可用)。您可以通過應用組策略或本地安全策略來執行此操作。您還必須對要監視的文件啓用審覈。您可以像修改文件權限一樣進行操作。

然後將審計事件寫入安全事件日誌。您必須監視此事件日誌以瞭解您感興趣的審計事件。一種方法是創建一個計劃任務,以便在您感興趣的事件記錄時啓動應用程序。對於每個事件啓動一個新的過程只有在事件沒有以很高的速度記錄時纔是可行的。否則,您可能會遇到性能問題。

基本上,你不想看文件的內容或屬性(shell函數GetFileDetails所做的)。此外,您不希望使用文件共享API來獲取打開該文件的網絡用戶(其中​​NetGetFileInfo會這樣做)。你想知道上次修改文件的進程的用戶。 Windows通常不會記錄此信息,因爲這需要太多資源才能完成所有文件活動。相反,您可以選擇性地啓用對特定用戶進行審計,以對特定文件(和文件夾)執行特定操作。

+0

你是否在說我自己將事件日誌寫入安全日誌? – 2012-07-25 23:46:09

+0

@KevinQu:當您在NTFS上啓用審覈時,操作系統會將審覈事件寫入安全事件日誌。您必須正確配置審計,然後監視安全事件日誌以查看這些審計事件。 – 2012-07-25 23:49:03

+0

我確實瀏覽了安全日誌。即使只創建一個文件也會導致超過10個日誌。而C#中的EventLog類在這方面沒有多大幫助 – 2012-07-25 23:52:49

2

看來你需要調用Windows API函數來獲得你想要的,這涉及PInvoke。有人在另一個論壇上一直在研究它,並認爲有東西出來,你可以找到他們的solution here。但是,它似乎只適用於網絡共享上的文件(不在本地計算機上)。

以供將來參考,這是code posted by 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); 
} 
+0

有沒有辦法可以爲本地機器做? – 2012-07-26 00:03:03

+0

我不認爲這是可能的。網上有很多人問同樣的問題,但他們都沒有答案。 – Virtlink 2012-07-26 00:14:00

2

這已被討論過很多次。我的回答來自同一個問題:

不能與FileSystemWatcher異步執行此操作,但可以使用文件系統過濾器驅動程序同步執行此操作。該驅動程序可讓您獲取執行操作的帳戶的用戶名。

1

使用代碼posted by dave4dl並更新聲明struct FILE_INFO_3如下, 您可以監視創建和更新文件操作的用戶名(這就像是FileSystemWatcher和OpenFiles的組合。文件共享服務器的exe文件的功能)的

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