Saltar al contenido

Descubra el nombre de usuario (quién) archivo modificado en C #

Hacemos una revisión exhaustiva cada secciones de nuestra web con el objetivo de mostrarte en todo momento información certera y actualizada.

Solución:

No recuerdo dónde encontré este código, pero es una alternativa al uso de pInvoke, que creo que es un poco exagerado para esta tarea. Utilizar el FileSystemWatcher para ver la carpeta y cuando se activa un evento, puede averiguar qué usuario hizo el cambio de archivo usando este código:

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 es una referencia a la DLL: Microsoft Shell Controls And Automation - es una referencia COM

A continuación, se muestran algunos ejemplos de cómo se llama al método:

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

O junte varias propiedades separadas por comas:

string SizeTypeAndLastModDate = GetSpecificFileProperties(filePath, new int[] 1, 2, 3);

Nota: Esta solución se ha probado en Windows 7 y Windows 10. No funcionará a menos que se ejecute en un STA según la excepción al usar Shell32 para obtener las propiedades extendidas del archivo y verá el siguiente error:

No se puede convertir el objeto COM del tipo 'Shell32.ShellClass' al tipo de interfaz 'Shell32.IShellDispatch6'

Debe habilitar la auditoría en el sistema de archivos (y la auditoría solo está disponible en NTFS). Para ello, aplique una política de grupo o una política de seguridad local. También deberá habilitar la auditoría en el archivo que desea monitorear. Lo hace de la misma manera que modifica los permisos en el archivo.

A continuación, los eventos de auditoría se escriben en el registro de eventos de seguridad. Tendrá que monitorear este registro de eventos para los eventos de auditoría que le interesan. Una forma de hacerlo es crear una tarea programada que inicie una aplicación cuando se registren los eventos que le interesan. Sin embargo, comenzar un nuevo proceso para cada evento solo es viable si los eventos no se registran a un ritmo muy alto. De lo contrario, es probable que experimente problemas de rendimiento.

Básicamente, no desea mirar el contenido o attributes del archivo (que la función de shell GetFileDetails lo hace). Además, no desea utilizar una API para compartir archivos para obtener el usuario de red que tiene el archivo abierto (que NetGetFileInfo lo hace). Desea conocer el usuario del proceso que modificó el archivo por última vez. Normalmente, Windows no registra esta información porque requeriría demasiados recursos para hacer eso para todas las actividades del archivo. En su lugar, puede habilitar selectivamente la auditoría para usuarios específicos que realizan acciones específicas en archivos (y carpetas) específicos.

Parece que necesitará invocar las funciones de la API de Windows para obtener lo que desea, lo que implica PInvoke. Algunas personas en otro foro lo han estado investigando y se han imaginado alguna cosa , puede encontrar su solución aquí. Sin embargo, parece funcionar solo con archivos en recursos compartidos de red (no en su máquina local).

Para referencia futura, este es el código publicado por 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);

Eres capaz de añadir valor a nuestra información asistiendo con tu experiencia en las explicaciones.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *