[Win32utils-devel] GetFinalPathNameByHandle for XP and earlier

Daniel Berger djberg96 at gmail.com
Sat Nov 22 03:17:52 EST 2008


Hi all,

How's this look? I based it on 
http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx.

I'm undecided as to when I should raise an error versus when I should just let 
it fall through, but this is how it is for now.

You'll need the latest windows-pr from CVS, btw.

Regards,

Dan

PS - Where should I put it? In Windows::File directly? In a separate helper module?

require 'windows/file'
require 'windows/file_mapping'
require 'windows/volume'
require 'windows/process'
require 'windows/limits'
require 'windows/handle'
require 'windows/error'
require 'windows/memory'

include Windows::File
include Windows::FileMapping
include Windows::Volume
include Windows::Process
include Windows::Limits
include Windows::Handle
include Windows::Error
include Windows::Memory

# The buf, buf_size, and flags arguments are ignored, but are
# present in order to keep the function parameters identical
# to the function defined for Windows Vista and later.
#
def GetFinalPathNameByHandle(handle, buf, buf_size, flags)
    size_ptr = [0].pack('Q')

    unless GetFileSizeEx(handle, size_ptr)
       raise get_last_error
    end

    if size_ptr.unpack('Q')[0] <= 0
       raise 'file size must be greater than zero'
    end

    map = CreateFileMapping(handle, nil, PAGE_READONLY, 0, 1, nil)
    final_path = nil

    if map && map > 0
       pmem = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 1)

       if pmem
          begin
             buf = 0.chr * MAXPATH

             # Buf will contain the full path, but with the device name instead
             # of the drive letter that we ultimately want.
             if GetMappedFileName(GetCurrentProcess(), pmem, buf, MAXPATH) > 0
                buf.strip!
                dbuf = 0.chr * 512

                # Get a list of logical devices
                if GetLogicalDriveStrings(dbuf.size, dbuf) > 0
                   devices = dbuf.split("\0")
                   devices.delete_if{ |d| d.empty? || d.size > 3 }

                   # Match the drive letter to a device. Once we find a match to
                   # the device returned by GetMappedFileName, replace the device
                   # name from our original buffer with the drive letter.
                   devices.each{ |device|
                      name = 0.chr * MAXPATH
                      device.chop! # Remove trailing slash

                      if QueryDosDevice(device, name, name.size) > 0
                         name.strip!
                         if buf.include?(name)
                            final_path = device << buf[name.length..-1]
                            break
                         end
                      end
                   }
                end
             else
                raise get_last_error
             end
          ensure
             UnmapViewOfFile(pmem)
          end
       end
    else
       raise get_last_error
    end

    final_path
end

# Test program

file = File.join(Dir.pwd, 'test.txt')

handle = CreateFile(
    file,
    GENERIC_READ | GENERIC_WRITE,
    FILE_SHARE_READ,
    nil,
    OPEN_ALWAYS,
    nil,
    nil
)

if handle == INVALID_HANDLE_VALUE
    raise get_last_error
end

p GetFinalPathNameByHandle(handle, nil, nil, nil)

CloseHandle(handle)


More information about the win32utils-devel mailing list