FileSystemWatcher を使用してフォルダーを監視しています。しかし、ディレクトリで何らかのイベントが発生した場合、そのファイルに誰が影響を与えたのかを検索する方法がわかりません。EventLog を使用しようとしました。それはうまくいきませんでした。それを行う別の方法はありますか?
5 に答える
このコードを見つけた場所を思い出せませんが、このタスクには少しやり過ぎだと思う 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);
または、コンマで区切られた複数のプロパティをまとめて取得します。
string SizeTypeAndLastModDate = GetSpecificFileProperties(filePath, new int[] {1, 2, 3});
注: このソリューションは、Windows 7 および Windows 10 でテストされています。Shell32 を使用してファイルの拡張プロパティを取得する場合の例外に従って、STA で実行しない限り機能しません。次のエラーが表示されます。
タイプ 'Shell32.ShellClass' の COM オブジェクトをインターフェイス タイプ 'Shell32.IShellDispatch6' にキャストできません
ファイル システムで監査を有効にする必要があります (監査は NTFS でのみ使用できます)。これを行うには、グループ ポリシーまたはローカル セキュリティ ポリシーを適用します。また、監視するファイルの監査を有効にする必要があります。ファイルのアクセス許可を変更するのと同じ方法で行います。
その後、監査イベントがセキュリティ イベント ログに書き込まれます。関心のある監査イベントについて、このイベント ログを監視する必要があります。これを行う 1 つの方法は、関心のあるイベントがログに記録されたときにアプリケーションを開始するスケジュールされたタスクを作成することです。ただし、イベントが非常に高いレートで記録されていない場合にのみ、イベントごとに新しいプロセスを開始できます。そうしないと、パフォーマンスの問題が発生する可能性があります。
基本的に、ファイルの内容や属性を調べたくありません (シェル関数が行いますGetFileDetails
)。また、ファイルを開いているネットワーク ユーザーを取得するためにファイル共有 API を使用する必要はありません (実際に開いていますNetGetFileInfo
)。ファイルを最後に変更したプロセスのユーザーを知りたい。通常、この情報は Windows によって記録されません。これは、すべてのファイル アクティビティに対して記録するには非常に多くのリソースが必要になるためです。代わりに、特定のファイル (およびフォルダー) に対して特定のアクションを実行している特定のユーザーの監査を選択的に有効にすることができます。
必要なものを取得するには、Windows API 関数を呼び出す必要があるようです。これには、PInvoke が含まれます。別のフォーラムの何人かの人々がそれを調べて何かを考え出しました。彼らの解決策はここで見つけることができます. ただし、(ローカル マシンではなく) ネットワーク共有上のファイルでのみ機能するようです。
今後の参考のために、これは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);
}
これは何度も議論されてきました。同じ質問からの私の答え:
これを FileSystemWatcher で非同期的に行うことはできませんが、ファイル システム フィルター ドライバーを使用して同期的に行うことはできます。ドライバーを使用すると、操作を実行しているアカウントのユーザー名を取得できます。
dave4dl によって投稿されたコードを使用し、構造体 FILE_INFO_3 を次のように更新宣言すると、ファイルの作成および更新アクションのユーザー名を監視できます (FileSystemWatcher と FileSharing Server の 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;
}