2008-09-15 7 views

Respuesta

0

En unixes no hay una forma real de hacerlo de manera confiable. En Unix con el sistema de archivos tradicional Unix, puede abrir un archivo y luego desvincularlo (eliminar su entrada del directorio) y usarlo, momento en el que el nombre no se almacena en ningún lugar. Además, debido a que un archivo puede tener enlaces duros múltiples en el sistema de archivos, cada uno de los nombres es equivalente, por lo que una vez que tenga solo el manejador abierto, no estará claro a qué nombre de archivo debe asignar el mapa.

Por lo tanto, puede hacer esto en Win32 usando las otras respuestas, pero si alguna vez necesita portar la aplicación a un entorno Unix, no tendrá suerte. Mi consejo para usted es refactorizar su programa, si es posible, para que no necesite el SO para poder mantener una conexión de recurso abierto a nombre de archivo.

4

edición Gracias por los comentarios acerca de este ser única Vista o Server 2008. Me perdí eso en la página. Supongo que debería haber leído el entero artículo;)

Parece que puede utilizar GetFileInformationByHandleEx() para obtener esta información.

Es probable que desee hacer algo como:

GetFileInformationByHandleEx(fileHandle, FILE_NAME_INFO, lpFileInformation, sizeof(FILE_NAME_INFO)); 

doble comprobar la página de MSDN para asegurarse de que no te he engañado tan mal :)

Cheers,

Taylor

+0

El único problema con esta solución es que GetFileInformationByHandleEx requiere Windows Vista o Servidor 2008 (o posterior). – ChrisN

+3

Me encanta la simplicidad de este, pero estoy en XP :-( –

+1

Parece simple, pero devuelve solo la ruta + nombre del archivo, pero falta la unidad, por lo que no es realmente útil. – Elmue

0

Si necesita hacer esto en Win32 pre-Vista o Server 2008, mire la función GetMappedFileName(...), que es uno de los secretos mejor guardados en Win32. Con un poco de C/C++-fu, puede mapear una pequeña porción del archivo en cuestión y luego pasar ese manejador a esta función.

Además, en Win32, no puede realmente eliminar un archivo que está abierto (el problema de abrir/desvincular mencionado en otra respuesta); puede marcarlo para su eliminación al cerrarlo, pero seguirá funcionando hasta su último comando abierto está cerrado. No sé si el mapeo (a través de mmap(...)) el archivo en este caso ayudaría, porque tiene que apuntar a un archivo físico ...

- = - James.

1

Fwiw, aquí está la misma solución del artículo de MSDN sugerido por Prakash en Python usando la maravillosa ctypes:

from ctypes import * 
# get handle to c:\boot.ini to test 
handle = windll.kernel32.CreateFileA("c:\\boot.ini", 0x80000000, 3, 0, 3, 0x80, 0) 
hfilemap = windll.kernel32.CreateFileMappingA(handle, 0, 2, 0, 1, 0) 
pmem = windll.kernel32.MapViewOfFile(hfilemap, 4, 0, 0, 1) 
name = create_string_buffer(1024) 
windll.psapi.GetMappedFileNameA(windll.kernel32.GetCurrentProcess(), pmem, name, 1024) 
print "The name for the handle 0x%08x is %s" % (handle, name.value) 
# convert device name to drive letter 
buf = create_string_buffer(512) 
size = windll.kernel32.GetLogicalDriveStringsA(511, buf) 
names = buf.raw[0:size-1].split("\0") 
for drive in names: 
    windll.kernel32.QueryDosDeviceA(drive[0:2], buf, 512) 
    if name.value.startswith(buf.value): 
     print "%s%s" % (drive[0:2], name.value[len(buf.value):]) 
     break 
+0

Este código funciona pero tiene algunas limitaciones. Ver mi publicación aquí – Elmue

13

Hay una correcta (aunque no documentado) manera de hacer esto en Windows XP que también funciona con directorios - el mismo método que usa GetFinalPathNameByHandle en Windows Vista y posterior.

Aquí están las declaraciones intermedias.Algunos de ellos ya están en WInternl.h y MountMgr.h pero simplemente los puso aquí de todos modos:

#include "stdafx.h" 
#include <Windows.h> 
#include <assert.h> 

enum OBJECT_INFORMATION_CLASS { ObjectNameInformation = 1 }; 
enum FILE_INFORMATION_CLASS { FileNameInformation = 9 }; 
struct FILE_NAME_INFORMATION { ULONG FileNameLength; WCHAR FileName[1]; }; 
struct IO_STATUS_BLOCK { PVOID Dummy; ULONG_PTR Information; }; 
struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; }; 
struct MOUNTMGR_TARGET_NAME { USHORT DeviceNameLength; WCHAR DeviceName[1]; }; 
struct MOUNTMGR_VOLUME_PATHS { ULONG MultiSzLength; WCHAR MultiSz[1]; }; 

extern "C" NTSYSAPI NTSTATUS NTAPI NtQueryObject(IN HANDLE Handle OPTIONAL, 
    IN OBJECT_INFORMATION_CLASS ObjectInformationClass, 
    OUT PVOID ObjectInformation OPTIONAL, IN ULONG ObjectInformationLength, 
    OUT PULONG ReturnLength OPTIONAL); 
extern "C" NTSYSAPI NTSTATUS NTAPI NtQueryInformationFile(IN HANDLE FileHandle, 
    OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, 
    IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass); 

#define MOUNTMGRCONTROLTYPE ((ULONG) 'm') 
#define IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH \ 
    CTL_CODE(MOUNTMGRCONTROLTYPE, 12, METHOD_BUFFERED, FILE_ANY_ACCESS) 

union ANY_BUFFER { 
    MOUNTMGR_TARGET_NAME TargetName; 
    MOUNTMGR_VOLUME_PATHS TargetPaths; 
    FILE_NAME_INFORMATION NameInfo; 
    UNICODE_STRING UnicodeString; 
    WCHAR Buffer[USHRT_MAX]; 
}; 

Aquí está la función básica:

LPWSTR GetFilePath(HANDLE hFile) 
{ 
    static ANY_BUFFER nameFull, nameRel, nameMnt; 
    ULONG returnedLength; IO_STATUS_BLOCK iosb; NTSTATUS status; 
    status = NtQueryObject(hFile, ObjectNameInformation, 
     nameFull.Buffer, sizeof(nameFull.Buffer), &returnedLength); 
    assert(status == 0); 
    status = NtQueryInformationFile(hFile, &iosb, nameRel.Buffer, 
     sizeof(nameRel.Buffer), FileNameInformation); 
    assert(status == 0); 
    //I'm not sure how this works with network paths... 
    assert(nameFull.UnicodeString.Length >= nameRel.NameInfo.FileNameLength); 
    nameMnt.TargetName.DeviceNameLength = (USHORT)(
     nameFull.UnicodeString.Length - nameRel.NameInfo.FileNameLength); 
    wcsncpy(nameMnt.TargetName.DeviceName, nameFull.UnicodeString.Buffer, 
     nameMnt.TargetName.DeviceNameLength/sizeof(WCHAR)); 
    HANDLE hMountPointMgr = CreateFile(_T("\\\\.\\MountPointManager"), 
     0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
     NULL, OPEN_EXISTING, 0, NULL); 
    __try 
    { 
     DWORD bytesReturned; 
     BOOL success = DeviceIoControl(hMountPointMgr, 
      IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH, &nameMnt, 
      sizeof(nameMnt), &nameMnt, sizeof(nameMnt), 
      &bytesReturned, NULL); 
     assert(success && nameMnt.TargetPaths.MultiSzLength > 0); 
     wcsncat(nameMnt.TargetPaths.MultiSz, nameRel.NameInfo.FileName, 
      nameRel.NameInfo.FileNameLength/sizeof(WCHAR)); 
     return nameMnt.TargetPaths.MultiSz; 
    } 
    __finally { CloseHandle(hMountPointMgr); } 
} 

y he aquí un ejemplo de uso:

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    HANDLE hFile = CreateFile(_T("\\\\.\\C:\\Windows\\Notepad.exe"), 
     0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 
    assert(hFile != NULL && hFile != INVALID_HANDLE_VALUE); 
    __try 
    { 
     wprintf(L"%s\n", GetFilePath(hFile)); 
     // Prints: 
     // C:\Windows\notepad.exe 
    } 
    __finally { CloseHandle(hFile); } 
    return 0; 
} 
+1

'MAYÚSCULAS/minúsculas' == RATIO WAY DEMASIADO GRANDE – pmg

+2

@pmg: Estoy de acuerdo, pero no elegí los nombres ... la API de Windows ya es así, solo copié/pegué lo que necesitaba aquí. – Mehrdad

+0

+1, creo que esto funciona aunque se necesita compilar DDK. – ybungalobill

13

I intenté el código publicado por Mehrdad aquí. Funciona, pero con limitaciones:

  1. No debe utilizarse para recursos compartidos de red porque MountPointManager puede bloquearse durante mucho tiempo.
  2. Se utiliza la API no documentada (IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH) No me gusta mucho
  3. No es compatible con dispositivos USB que crean puertos virtuales COM (necesito que en mi proyecto)

también estudié otra se acerca a GetFileInformationByHandleEx() y GetFinalPathNameByHandle(), pero son inútiles, ya que solo devuelven Path + Filename pero sin unidad. Además, GetFinalPathNameByHandle() también tiene el error de suspensión.

El enfoque GetMappedFileName() en el MSDN (publicado por Max aquí) también es muy limitado:

  1. Sólo funciona con archivos reales
  2. El tamaño del archivo no debe ser cero bytes
  3. Directorios, Red y puertos COM no son compatibles
  4. El código es torpe

así que escribí mi propio código. Lo probé en Win XP y en Win 7, 8 y 10. Funciona perfectamente.

NOTA: ¡NO necesita ningún archivo LIB adicional para compilar este código!

CPP ARCHIVO:

t_NtQueryObject NtQueryObject() 
{ 
    static t_NtQueryObject f_NtQueryObject = NULL; 
    if (!f_NtQueryObject) 
    { 
     HMODULE h_NtDll = GetModuleHandle(L"Ntdll.dll"); // Ntdll is loaded into EVERY process! 
     f_NtQueryObject = (t_NtQueryObject)GetProcAddress(h_NtDll, "NtQueryObject"); 
    } 
    return f_NtQueryObject; 
} 


// returns 
// "\Device\HarddiskVolume3"        (Harddisk Drive) 
// "\Device\HarddiskVolume3\Temp"       (Harddisk Directory) 
// "\Device\HarddiskVolume3\Temp\transparent.jpeg"   (Harddisk File) 
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg"     (USB stick) 
// "\Device\TrueCryptVolumeP\Data\Passwords.txt"   (Truecrypt Volume) 
// "\Device\Floppy0\Autoexec.bat"       (Floppy disk) 
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB"     (DVD drive) 
// "\Device\Serial1"          (real COM port) 
// "\Device\USBSER000"          (virtual COM port) 
// "\Device\Mup\ComputerName\C$\Boot.ini"     (network drive share, Windows 7) 
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini"  (network drive share, Windwos XP) 
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" (network folder share, Windwos XP) 
// "\Device\Afd"           (internet socket) 
// "\Device\Console000F"         (unique name for any Console handle) 
// "\Device\NamedPipe\Pipename"        (named pipe) 
// "\BaseNamedObjects\Objectname"       (named mutex, named event, named semaphore) 
// "\REGISTRY\MACHINE\SOFTWARE\Classes\.txt"    (HKEY_CLASSES_ROOT\.txt) 
DWORD GetNtPathFromHandle(HANDLE h_File, CString* ps_NTPath) 
{ 
    if (h_File == 0 || h_File == INVALID_HANDLE_VALUE) 
     return ERROR_INVALID_HANDLE; 

    // NtQueryObject() returns STATUS_INVALID_HANDLE for Console handles 
    if (IsConsoleHandle(h_File)) 
    { 
     ps_NTPath->Format(L"\\Device\\Console%04X", (DWORD)(DWORD_PTR)h_File); 
     return 0; 
    } 

    BYTE u8_Buffer[2000]; 
    DWORD u32_ReqLength = 0; 

    UNICODE_STRING* pk_Info = &((OBJECT_NAME_INFORMATION*)u8_Buffer)->Name; 
    pk_Info->Buffer = 0; 
    pk_Info->Length = 0; 

    // IMPORTANT: The return value from NtQueryObject is bullshit! (driver bug?) 
    // - The function may return STATUS_NOT_SUPPORTED although it has successfully written to the buffer. 
    // - The function returns STATUS_SUCCESS although h_File == 0xFFFFFFFF 
    NtQueryObject()(h_File, ObjectNameInformation, u8_Buffer, sizeof(u8_Buffer), &u32_ReqLength); 

    // On error pk_Info->Buffer is NULL 
    if (!pk_Info->Buffer || !pk_Info->Length) 
     return ERROR_FILE_NOT_FOUND; 

    pk_Info->Buffer[pk_Info->Length /2] = 0; // Length in Bytes! 

    *ps_NTPath = pk_Info->Buffer; 
    return 0; 
} 

// converts 
// "\Device\HarddiskVolume3"        -> "E:" 
// "\Device\HarddiskVolume3\Temp"       -> "E:\Temp" 
// "\Device\HarddiskVolume3\Temp\transparent.jpeg"   -> "E:\Temp\transparent.jpeg" 
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg"     -> "I:\foto.jpg" 
// "\Device\TrueCryptVolumeP\Data\Passwords.txt"   -> "P:\Data\Passwords.txt" 
// "\Device\Floppy0\Autoexec.bat"       -> "A:\Autoexec.bat" 
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB"     -> "H:\VIDEO_TS\VTS_01_0.VOB" 
// "\Device\Serial1"          -> "COM1" 
// "\Device\USBSER000"          -> "COM4" 
// "\Device\Mup\ComputerName\C$\Boot.ini"     -> "\\ComputerName\C$\Boot.ini" 
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini"  -> "\\ComputerName\C$\Boot.ini" 
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" -> "\\ComputerName\Shares\Dance.m3u" 
// returns an error for any other device type 
DWORD GetDosPathFromNtPath(const WCHAR* u16_NTPath, CString* ps_DosPath) 
{ 
    DWORD u32_Error; 

    if (wcsnicmp(u16_NTPath, L"\\Device\\Serial", 14) == 0 || // e.g. "Serial1" 
     wcsnicmp(u16_NTPath, L"\\Device\\UsbSer", 14) == 0) // e.g. "USBSER000" 
    { 
     HKEY h_Key; 
     if (u32_Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Hardware\\DeviceMap\\SerialComm", 0, KEY_QUERY_VALUE, &h_Key)) 
      return u32_Error; 

     WCHAR u16_ComPort[50]; 

     DWORD u32_Type; 
     DWORD u32_Size = sizeof(u16_ComPort); 
     if (u32_Error = RegQueryValueEx(h_Key, u16_NTPath, 0, &u32_Type, (BYTE*)u16_ComPort, &u32_Size)) 
     { 
      RegCloseKey(h_Key); 
      return ERROR_UNKNOWN_PORT; 
     } 

     *ps_DosPath = u16_ComPort; 
     RegCloseKey(h_Key); 
     return 0; 
    } 

    if (wcsnicmp(u16_NTPath, L"\\Device\\LanmanRedirector\\", 25) == 0) // Win XP 
    { 
     *ps_DosPath = L"\\\\"; 
     *ps_DosPath += (u16_NTPath + 25); 
     return 0; 
    } 

    if (wcsnicmp(u16_NTPath, L"\\Device\\Mup\\", 12) == 0) // Win 7 
    { 
     *ps_DosPath = L"\\\\"; 
     *ps_DosPath += (u16_NTPath + 12); 
     return 0; 
    } 

    WCHAR u16_Drives[300]; 
    if (!GetLogicalDriveStrings(300, u16_Drives)) 
     return GetLastError(); 

    WCHAR* u16_Drv = u16_Drives; 
    while (u16_Drv[0]) 
    { 
     WCHAR* u16_Next = u16_Drv +wcslen(u16_Drv) +1; 

     u16_Drv[2] = 0; // the backslash is not allowed for QueryDosDevice() 

     WCHAR u16_NtVolume[1000]; 
     u16_NtVolume[0] = 0; 

     // may return multiple strings! 
     // returns very weird strings for network shares 
     if (!QueryDosDevice(u16_Drv, u16_NtVolume, sizeof(u16_NtVolume) /2)) 
      return GetLastError(); 

     int s32_Len = (int)wcslen(u16_NtVolume); 
     if (s32_Len > 0 && wcsnicmp(u16_NTPath, u16_NtVolume, s32_Len) == 0) 
     { 
      *ps_DosPath = u16_Drv; 
      *ps_DosPath += (u16_NTPath + s32_Len); 
      return 0; 
     } 

     u16_Drv = u16_Next; 
    } 
    return ERROR_BAD_PATHNAME; 
} 

encabezado del archivo:

#pragma warning(disable: 4996) // wcsnicmp deprecated 
#include <winternl.h> 

// This makro assures that INVALID_HANDLE_VALUE (0xFFFFFFFF) returns FALSE 
#define IsConsoleHandle(h) (((((ULONG_PTR)h) & 0x10000003) == 0x3) ? TRUE : FALSE) 

enum OBJECT_INFORMATION_CLASS 
{ 
    ObjectBasicInformation, 
    ObjectNameInformation, 
    ObjectTypeInformation, 
    ObjectAllInformation, 
    ObjectDataInformation 
}; 

struct OBJECT_NAME_INFORMATION 
{ 
    UNICODE_STRING Name; // defined in winternl.h 
    WCHAR NameBuffer; 
}; 

typedef NTSTATUS (NTAPI* t_NtQueryObject)(HANDLE Handle, OBJECT_INFORMATION_CLASS Info, PVOID Buffer, ULONG BufferSize, PULONG ReturnLength); 
+0

En primer lugar, este código necesita al menos kernel32.lib y advapi32. lib para compilar. Parece que Elmue "quería decir" que este código no necesita bibliotecas adicionales (por ejemplo: ntdll.lib) para compilar. :) En segundo lugar, este código no es perfecto para todos los redirectores de red (los proveedores de UNC), para ejemplo, en el caso de VirtualBox (host) compartir (Device \ VBoxMiniRdr como un destino de (DosDevices \\) VBoxMiniRdrDN enlace simbólico). :) – Mosc

+1

Entonces, ¿cuál es su propuesta para que funcione con VirtualBox? – Elmue

+2

Buena respuesta. Esto es básicamente lo que Process Hacker hace (y probablemente también el Explorador de procesos de Sysinternals) y funciona para prácticamente todos los tipos de manejadores y no solo los archivos. – conio

0

Para Windows Vista y más tarde yo prefiero usar GetFinalPathNameByHandle()

char buf[MAX_PATH]; 
GetFinalPathNameByHandleA(fileHandle, buf, sizeof(buf), VOLUME_NAME_DOS) 

Para Windows XP prefiero el solution by Mehrdad.

Entonces me carga GetFinalPathNameByHandle() de forma dinámica a través de GetProcAddress() y si esto falla (porque es Windows XP) voy por la solución de Mehrdad con NtQueryObject()

Cuestiones relacionadas