2008-09-15 29 views

Trả lời

0

Trên các bản sao không có cách thực sự đáng tin cậy để thực hiện việc này. Trong unix với hệ thống tập tin unix truyền thống, bạn có thể mở một tệp và sau đó hủy liên kết tệp đó (loại bỏ mục nhập của nó khỏi thư mục) và sử dụng nó, tại thời điểm đó tên không được lưu trữ ở bất kỳ đâu. Ngoài ra, bởi vì một tệp có thể có nhiều liên kết cứng vào hệ thống tệp, mỗi tên đều tương đương nhau, vì vậy khi bạn chỉ có trình xử lý mở, sẽ không rõ tên tệp mà bạn nên ánh xạ trở lại.

Vì vậy, bạn có thể làm điều này trên Win32 bằng cách sử dụng các câu trả lời khác, nhưng nếu bạn cần phải chuyển ứng dụng sang một môi trường unix, bạn sẽ không may mắn. Lời khuyên của tôi cho bạn là cấu trúc lại chương trình của bạn, nếu có thể, để bạn không cần hệ điều hành để có thể duy trì một tài nguyên mở để kết nối tên tệp.

4

chỉnh sửa Cảm ơn nhận xét về việc này chỉ là Vista hoặc Server 2008. Tôi đã bỏ lỡ điều đó trong trang. Đoán tôi đã đọc toàn bộ bài viết;)

Có vẻ như bạn có thể sử dụng GetFileInformationByHandleEx() để nhận thông tin này.

Bạn có thể sẽ muốn làm điều gì đó như:

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

Kiểm tra trang MSDN để chắc chắn rằng tôi đã không bị nhầm lẫn bạn quá nặng :)

Chúc mừng,

Taylor

+0

Vấn đề duy nhất với giải pháp này là GetFileInformationByHandleEx yêu cầu Windows Vista hoặc Máy chủ 2008 (hoặc mới hơn). – ChrisN

+3

Yêu sự đơn giản của trang này nhưng tôi đang sử dụng XP :-( –

+1

Có vẻ đơn giản nhưng nó chỉ trả về đường dẫn + tên tệp, nhưng ổ đĩa bị thiếu, vì vậy không thực sự hữu ích. – Elmue

0

Nếu bạn cần thực hiện điều này trên Win32 trước Vista hoặc Server 2008, hãy xem hàm GetMappedFileName(...), đây là một trong những bí mật được giữ kín nhất trong Win32. Với một chút C/C++-fu, bạn có thể nhớ ánh xạ một phần nhỏ của tệp được đề cập và sau đó chuyển điều khiển đó cho hàm này. Ngoài ra, trên Win32, bạn không thể thực sự xóa một tập tin đang mở (vấn đề mở/bỏ liên kết được đề cập trên câu trả lời khác) - bạn có thể đánh dấu nó để xóa khi đóng, nhưng nó vẫn sẽ treo xung quanh cho đến khi tay cầm mở cuối cùng của nó đã đóng. Dunno nếu ánh xạ (qua mmap(...)) tệp trong trường hợp này sẽ hữu ích, vì tệp phải trỏ ngược lại vào tệp vật lý ...

- = - James.

1

FWIW, đây là giải pháp tương tự từ các bài viết MSDN được đề xuất bởi Prakash bằng Python sử dụng tuyệt vời 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

Mã này hoạt động nhưng có một số hạn chế. Xem bài viết của tôi ở đây – Elmue

13

Có một chính xác (mặc dù không có giấy tờ) cách để làm điều này trên Windows XP mà cũng làm việc với các thư mục - cùng một phương thức GetFinalPathNameByHandle sử dụng trên Windows Vista trở lên.

Dưới đây là các khai báo được nêu ra.Một số trong số này là đã có trong WInternl.hMountMgr.h nhưng tôi chỉ cần đặt chúng ở đây anyway:

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

Dưới đây là các chức năng cốt lõi:

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

và đây là một cách sử dụng ví dụ:

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

'UPPERCASE/lowercase' == RATIO WAY QUÁ LỚN – pmg

+2

@pmg: Tôi đồng ý, nhưng tôi không chọn tên ... API Windows đã giống như vậy, tôi chỉ sao chép/dán những gì tôi cần đây. – Mehrdad

+0

+1, tôi tin rằng điều này hoạt động mặc dù một trong những nhu cầu DDK để biên dịch. – ybungalobill

13

tôi đã thử mã được đăng bởi Mehrdad tại đây. Tính năng này hoạt động nhưng có giới hạn:

  1. Không nên sử dụng chia sẻ mạng vì MountPointManager có thể bị treo trong một thời gian rất dài.
  2. Nó sử dụng API không có giấy tờ (IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH) Tôi không thích điều đó rất nhiều
  3. Nó không hỗ trợ các thiết bị USB mà tạo cổng COM ảo (Tôi cần điều đó trong dự án của tôi)

Tôi cũng đã nghiên cứu khác phương pháp tiếp cận như GetFileInformationByHandleEx()GetFinalPathNameByHandle(), nhưng đây là vô ích khi họ chỉ trả về đường dẫn + tên tệp nhưng không có ổ đĩa. Ngoài ra GetFinalPathNameByHandle() cũng có lỗi treo.

Các GetMappedFileName() cách tiếp cận trong MSDN (được đăng bởi Max ở đây) cũng rất hạn chế:

  1. Nó chỉ hoạt động với các tập tin thực
  2. Kích thước tập tin không phải là zero byte
  3. Thư mục, Network và các cổng COM không được hỗ trợ
  4. Mã này vụng về

Vì vậy, tôi đã viết mã của riêng mình. Tôi đã thử nghiệm nó trên Win XP và trên Win 7, 8, và 10. Nó hoạt động hoàn hảo.

LƯU Ý: Bạn KHÔNG cần bất kỳ tệp LIB bổ sung nào để biên dịch mã này!

CPP FILE:

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

HEADER FILE:

#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

Trước hết, mã này cần ít nhất là kernel32.lib và advapi32. Có vẻ như Elmue "muốn nói" rằng mã này không cần thư viện bổ sung (ví dụ: ntdll.lib) để biên dịch. :) Thứ hai, mã này không hoàn hảo cho tất cả các redirectors mạng (các nhà cung cấp UNC), cho Ví dụ, trong trường hợp của VirtualBox (host) chia sẻ (Device \ VBoxMiniRdr như là một mục tiêu của (DosDevices \\) VBoxMiniRdrDN liên kết tượng trưng). :) – Mosc

+1

Vì vậy, đề xuất của bạn để làm cho nó hoạt động với VirtualBox là gì? – Elmue

+2

Câu trả lời hay. Về cơ bản, đây là những gì mà Process Hacker thực hiện (và có lẽ cả Process Explorer của Sysinternals) và làm việc cho hầu như tất cả các loại xử lý và không chỉ các tập tin. – conio

0

Đối với Windows Vista và sau đó tôi thích sử dụng GetFinalPathNameByHandle()

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

Đối với Windows XP, tôi thích số solution by Mehrdad.

Vì vậy, tôi tải GetFinalPathNameByHandle() tự động qua GetProcAddress() và nếu điều này không (vì đó là Windows XP) Tôi đi cho giải pháp Mehrdad với NtQueryObject()

Các vấn đề liên quan