2012-07-25 7 views
5

Ich verwende einen FileSystemWatcher, um einen Ordner zu überwachen. Aber wenn ein Ereignis in dem Verzeichnis passiert, weiß ich nicht, wie ich suchen soll, wer sich auf diese Datei auswirkt. Ich habe versucht, EventLog zu verwenden. Es konnte einfach nicht funktionieren. Gibt es einen anderen Weg, es zu tun?Finden Sie heraus, Benutzername (wer) Datei in C# geändert

+0

möglich Duplikat [verwenden, um die Filesystemwatcher-Klasse um den Benutzer zu dokumentieren, die Änderungen macht] (http://stackoverflow.com/questions/8649661/use-the-filesystemwatcher -class-to-document-the-user-wer-macht-die-änderungen) –

Antwort

7

Ich kann nicht erinnern, wo ich diesen Code gefunden, aber seine eine Alternative zur Verwendung von PInvoke, die ich denke, ein bisschen übertrieben für diese Aufgabe ist. Verwenden Sie den FileSystemWatcher den Ordner zu beobachten und, wenn ein Ereignis ausgelöst wird können Sie arbeiten heraus, welche Benutzer die Datei ändern mit diesem Code gemacht:

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 ist ein Verweis auf die DLL: Microsoft Shell-Bedienelemente und Automation - its a COM Referenz

Hier ist ein Beispiel der, wie Sie die Methode aufrufen:

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

Soweit ich weiß, erhält Index 10 von 'GetDetailsOf' den Titel des Dokuments, nicht den" letzten Benutzer ". –

+0

@ Jeremy Thompson Wie kann ich Shell32.shell verwenden? Es scheint, als wäre es kein Referenzproblem. Import-Ding hilft nicht –

+0

'Shell32.shell' ist verfügbar über ** Microsoft Shell Controls And Automation ** - es ist eine COM-Referenz. –

4

Sie müssen die Überwachung für das Dateisystem aktivieren (und Auditing ist nur für NTFS verfügbar). Dazu wenden Sie eine Gruppenrichtlinie oder lokale Sicherheitsrichtlinie an. Sie müssen außerdem die Überwachung für die Datei aktivieren, die Sie überwachen möchten. Sie tun es auf die gleiche Weise wie Sie die Berechtigungen für die Datei ändern.

Überwachungsereignisse werden dann in das Sicherheitsereignisprotokoll geschrieben. Sie müssen dieses Ereignisprotokoll auf die Überwachungsereignisse überwachen, an denen Sie interessiert sind. Eine Möglichkeit besteht darin, eine geplante Aufgabe zu erstellen, die eine Anwendung startet, wenn die Ereignisse protokolliert werden, an denen Sie interessiert sind. Das Starten eines neuen Prozesses für jedes Ereignis ist nur dann sinnvoll, wenn Ereignisse nicht mit einer sehr hohen Rate protokolliert werden. Andernfalls treten wahrscheinlich Leistungsprobleme auf.

Grundsätzlich wollen Sie nicht auf den Inhalt oder die Attribute der Datei schauen (was die Shell-Funktion GetFileDetails tut). Außerdem möchten Sie keine Dateifreigabe-API verwenden, um den Netzwerkbenutzer zu erhalten, der die Datei geöffnet hat (was NetGetFileInfo tut). Sie möchten den Benutzer des Prozesses kennen, der die Datei zuletzt geändert hat. Diese Informationen werden normalerweise nicht von Windows aufgezeichnet, da zu viele Ressourcen für alle Dateiaktivitäten benötigt werden. Stattdessen können Sie selektiv die Überwachung für bestimmte Benutzer aktivieren, die bestimmte Aktionen für bestimmte Dateien (und Ordner) ausführen.

+0

sagst du write eventLog von mir selbst in den security even log? –

+0

@KevinQu: Wenn Sie die Überwachung für NTFS aktivieren, schreibt das Betriebssystem Überwachungsereignisse in das Sicherheitsereignisprotokoll. Sie müssen die Überwachung ordnungsgemäß konfigurieren und anschließend das Sicherheitsereignisprotokoll für diese Überwachungsereignisse überwachen. –

+0

Ich habe das Sicherheitsprotokoll durchsucht. Selbst eine einzige Erstellung der Datei wird mehr als 10 Protokolle verursachen. Und die EventLog-Klasse in C# hilft nicht zu sehr dabei –

2

Es scheint, dass Sie Windows API-Funktionen aufrufen müssen, um zu erhalten, was Sie wollen, was PInvoke beinhaltet. Einige Leute in einem anderen Forum haben es untersucht und haben etwas heraus gefunden, können Sie ihre solution here finden. Es scheint jedoch nur mit Dateien auf Netzwerkfreigaben (nicht auf Ihrem lokalen Computer) zu funktionieren.

Für die Zukunft ist dies die 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

gibt es einen Weg, der für die lokale Maschine tun kann? –

+0

Ich glaube nicht, dass es möglich ist. Es gibt viele Leute im Internet, die dasselbe fragen, aber keiner von ihnen hat irgendwelche Antworten. – Virtlink

2

Dies hat oft diskutiert worden. Meine Antwort von der gleichen Frage:

Sie können dies nicht asynchron mit FileSystemWatcher tun, aber Sie können dies synchron mit Dateisystem-Filtertreiber tun. Mit dem Treiber können Sie den Benutzernamen des Kontos abrufen, das den Vorgang ausführt.

1

Verwenden Sie den Code posted by dave4dl und aktualisieren Sie deklarieren struct FILE_INFO_3 wie folgt, Sie können Benutzernamen von erstellen und aktualisieren Dateiaktion überwachen (Es ist wie zu Kombination von FileSystemWatcher und OpenFiles.exe Funktionen von FileSharing Server)

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