traductor studio inventor inglés ingles idiomas hacer google español crear como app c windows winapi

studio - traductor google



Cómo obtener el nombre asociado con HANDLE abierto (7)

¿Cuál es la forma más fácil de obtener el nombre de archivo asociado con una MANIJA abierta en Win32?


En Unixes no hay una forma real de hacer esto 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.


FWIW, aquí está la misma solución del artículo de MSDN sugerido por Prakash en Python usando los maravillosos tipos :

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


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 en memoria una pequeña porción del archivo en cuestión, y luego pasar ese asa a esta función.

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

- = - James.


edit Gracias por los comentarios sobre esto siendo Vista o Server 2008 solamente. Me perdí eso en la página. Supongo que debería haber leído todo el artículo;)

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

Es probable que quieras hacer algo como:

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

Comprueba dos veces la página de MSDN para asegurarte de que no te he engañado demasiado :)

Aclamaciones,

Taylor


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

Aquí están las declaraciones encendidas. Algunos de estos ya están en WInternl.h MountMgr.h pero de todos modos los puse aquí:

#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 central:

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 aquí hay 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; }


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

  1. No debe usarse para recursos compartidos de red porque MountPointManager puede bloquearse durante mucho tiempo.
  2. Utiliza la API no documentada (IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH) No me gusta mucho
  3. No admite dispositivos USB que creen puertos COM virtuales (lo necesito en mi proyecto)

También estudié otros enfoques como GetFileInformationByHandleEx() y GetFinalPathNameByHandle() , pero estos son inútiles, ya que solo devuelven Path + Filename pero sin unidad. Además GetFinalPathNameByHandle() también tiene el error colgante.

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

  1. Funciona solo con archivos reales
  2. El tamaño del archivo no debe ser de cero bytes
  3. Los directorios, la red y los 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!

ARCHIVO CPP:

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

ARCHIVO DE CABECERA:

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


Para Windows Vista y versiones posteriores, prefiero usar GetFinalPathNameByHandle ()

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

Para Windows XP, prefiero la solución de Mehrdad .

Así que cargué GetFinalPathNameByHandle () dinámicamente a través de GetProcAddress () y si esto falla (porque es Windows XP) voy por la solución de Mehrdad con NtQueryObject ()