¿Cuál es la forma más fácil de obtener el nombre de archivo asociado con una MANIJA abierta en Win32?Cómo obtener el nombre asociado con MANIJA abierta
Respuesta
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.
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
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.
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
Este código funciona pero tiene algunas limitaciones. Ver mi publicación aquí – Elmue
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;
}
'MAYÚSCULAS/minúsculas' == RATIO WAY DEMASIADO GRANDE – pmg
@pmg: Estoy de acuerdo, pero no elegí los nombres ... la API de Windows ya es así, solo copié/pegué lo que necesitaba aquí. – Mehrdad
+1, creo que esto funciona aunque se necesita compilar DDK. – ybungalobill
I intenté el código publicado por Mehrdad aquí. Funciona, pero con limitaciones:
- No debe utilizarse para recursos compartidos de red porque MountPointManager puede bloquearse durante mucho tiempo.
- Se utiliza la API no documentada (IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH) No me gusta mucho
- 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:
- Sólo funciona con archivos reales
- El tamaño del archivo no debe ser cero bytes
- Directorios, Red y puertos COM no son compatibles
- 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);
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
Entonces, ¿cuál es su propuesta para que funcione con VirtualBox? – Elmue
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
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()
- 1. Erlang: obtener el "nombre registrado" asociado con un `pid`
- 2. ¿Cómo puedo obtener el nombre/índice de la interfaz asociado con un socket TCP?
- 3. Knockout.js obtener el objeto dom asociado con los datos
- 4. vi abierta con el nombre de archivo pasado
- 5. ¿Cómo obtener el conteo más alto del modelo asociado (Rails)?
- 6. XSLT: valor de texto asociado con el elemento
- 7. manija tiempo de espera :: Error con reintento
- 8. ¿Cómo se asigna el almacenamiento asociado con std :: future?
- 9. Obtenga el nombre de usuario de Windows asociado con un escritorio
- 10. señal de la manija con sigaction
- 11. ¿Hay un #define asociado con el SubSystem
- 12. manija en Quartz.net
- 13. ¿Cómo obtengo el ícono asociado con un tipo de archivo?
- 14. Cómo abrir un archivo con el programa asociado predeterminado
- 15. jQuery UI redimensionable - Cómo espesar la manija
- 16. Cómo cambiar el valor del campo asociado
- 17. cómo validar el id del modelo asociado?
- 18. ¿Cómo puedo obtener el nombre del elemento con jQuery?
- 19. Cómo puedo obtener el nombre de contacto con su número
- 20. ¿Cómo obtener el nombre de la constante?
- 21. imagen abierta con Instagram
- 22. Obtener el nombre del elemento
- 23. ¿Puedo obtener el icono asociado a una impresora instalada a través de .Net?
- 24. android, cómo obtener el nombre del paquete?
- 25. cómo obtener el nombre abreviado de TimeZoneInfo
- 26. Android: ¿Cómo obtener el nombre Disponibles imagen
- 27. Cómo obtener el nombre del país
- 28. ¿Cómo obtener el nombre completo del tipo?
- 29. ¿Cómo obtener el nombre de clase?
- 30. ¿Cómo obtener el último nombre de etiqueta?
El único problema con esta solución es que GetFileInformationByHandleEx requiere Windows Vista o Servidor 2008 (o posterior). – ChrisN
Me encanta la simplicidad de este, pero estoy en XP :-( –
Parece simple, pero devuelve solo la ruta + nombre del archivo, pero falta la unidad, por lo que no es realmente útil. – Elmue