none
Trying To Call GetFileInformationByHandle From C# RRS feed

  • Question

  • Hi,

    I am working on a file archiving project, and need to know how many (hard) links to a file. It looks like the only way to get this is by dropping to a C++ function GetFileInformationByHandle.

    Problem is, this looks pretty tricky. I have Googled extensively to no avail. Searching for MSDN magazine articles looks promising, but the links all seem 404. I looked at PInvoke.net, but the page for this function looks mangled (or plain wrong) with lots of "ToDo" entries.

    Does anyone have any boilerplate or working examples of how to make this work in managed code? I'm a bit of a newbie at C#, so this whole interop thing is a mystery. Thanks in advance.

    Thursday, April 25, 2019 5:36 AM

Answers

  • Simple example expanding on some scaffolding provided by Castorix31 -

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    
    namespace CSLinks
    {
        class Program
        {
            [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, IntPtr lpSECURITY_ATTRIBUTES, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
    
            [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool CloseHandle(IntPtr hObject);
    
            [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool GetFileInformationByHandle( IntPtr handle, ref BY_HANDLE_FILE_INFORMATION hfi);
    
            public const int INVALID_HANDLE_VALUE = -1;
    
            public const uint GENERIC_READ = 0x80000000;
            public const int ERROR_INSUFFICIENT_BUFFER = 122;
    
            public const int FILE_SHARE_READ = 1;
            public const int FILE_SHARE_WRITE = 2;
            public const int FILE_SHARE_DELETE = 4;
            public const int FILE_ATTRIBUTE_NORMAL = 0x80;
    
            public const int CREATE_NEW = 1;
            public const int CREATE_ALWAYS = 2;
            public const int OPEN_EXISTING = 3;
            public const int OPEN_ALWAYS = 4;
            public const int TRUNCATE_EXISTING = 5;
    
            [StructLayout(LayoutKind.Sequential)]
            public struct BY_HANDLE_FILE_INFORMATION
            {
                public uint dwFileAttributes;
                public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
                public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
                public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
                public uint dwVolumeSerialNumber;
                public uint nFileSizeHigh;
                public uint nFileSizeLow;
                public uint nNumberOfLinks;
                public uint nFileIndexHigh;
                public uint nFileIndexLow;
            };
    
            static void Main(string[] args)
            {
                BY_HANDLE_FILE_INFORMATION hfi = new BY_HANDLE_FILE_INFORMATION {};
    
                IntPtr handle = CreateFile(@"YourFile", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
                if(handle.ToInt32() != INVALID_HANDLE_VALUE)
                {
                    if (GetFileInformationByHandle(handle, ref hfi))
                        Console.WriteLine("Number of links is {0}", hfi.nNumberOfLinks);
                    else
                        Console.WriteLine("GetFileInformationByHandle error, code was {0}", Marshal.GetLastWin32Error());
    
                    CloseHandle(handle);
                }
                else
                    Console.WriteLine("CreateFile error, code was {0}", Marshal.GetLastWin32Error());
            }
        }
    }
    

    Thursday, April 25, 2019 3:17 PM

All replies

  • To get the link directly, you can use GetFinalPathNameByHandle :

    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern int GetFinalPathNameByHandle(IntPtr hFile, [Out] StringBuilder lpszFilePath, int cchFilePath, int dwFlags);
    
    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, IntPtr lpSECURITY_ATTRIBUTES, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
    
    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern bool CloseHandle(IntPtr hObject);
    
    public const int VOLUME_NAME_DOS = 0x0;     //default
    public const int VOLUME_NAME_GUID = 0x1;
    public const int VOLUME_NAME_NT = 0x2;
    public const int VOLUME_NAME_NONE = 0x4;
    
    public const int FILE_NAME_NORMALIZED = 0x0;  //default
    public const int FILE_NAME_OPENED = 0x8;
    
    public const int INVALID_HANDLE_VALUE = -1;
    
    public const uint GENERIC_READ = 0x80000000;
    public const int ERROR_INSUFFICIENT_BUFFER = 122;
    
    public const int FILE_SHARE_READ = 1;
    public const int FILE_SHARE_WRITE = 2;
    public const int FILE_SHARE_DELETE = 4;
    public const int FILE_ATTRIBUTE_NORMAL = 0x80;
    
    public const int CREATE_NEW = 1;
    public const int CREATE_ALWAYS = 2;
    public const int OPEN_EXISTING = 3;
    public const int OPEN_ALWAYS = 4;
    public const int TRUNCATE_EXISTING = 5;

    Thursday, April 25, 2019 7:24 AM
  • Simple example expanding on some scaffolding provided by Castorix31 -

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    
    namespace CSLinks
    {
        class Program
        {
            [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, IntPtr lpSECURITY_ATTRIBUTES, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
    
            [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool CloseHandle(IntPtr hObject);
    
            [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool GetFileInformationByHandle( IntPtr handle, ref BY_HANDLE_FILE_INFORMATION hfi);
    
            public const int INVALID_HANDLE_VALUE = -1;
    
            public const uint GENERIC_READ = 0x80000000;
            public const int ERROR_INSUFFICIENT_BUFFER = 122;
    
            public const int FILE_SHARE_READ = 1;
            public const int FILE_SHARE_WRITE = 2;
            public const int FILE_SHARE_DELETE = 4;
            public const int FILE_ATTRIBUTE_NORMAL = 0x80;
    
            public const int CREATE_NEW = 1;
            public const int CREATE_ALWAYS = 2;
            public const int OPEN_EXISTING = 3;
            public const int OPEN_ALWAYS = 4;
            public const int TRUNCATE_EXISTING = 5;
    
            [StructLayout(LayoutKind.Sequential)]
            public struct BY_HANDLE_FILE_INFORMATION
            {
                public uint dwFileAttributes;
                public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
                public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
                public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
                public uint dwVolumeSerialNumber;
                public uint nFileSizeHigh;
                public uint nFileSizeLow;
                public uint nNumberOfLinks;
                public uint nFileIndexHigh;
                public uint nFileIndexLow;
            };
    
            static void Main(string[] args)
            {
                BY_HANDLE_FILE_INFORMATION hfi = new BY_HANDLE_FILE_INFORMATION {};
    
                IntPtr handle = CreateFile(@"YourFile", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
                if(handle.ToInt32() != INVALID_HANDLE_VALUE)
                {
                    if (GetFileInformationByHandle(handle, ref hfi))
                        Console.WriteLine("Number of links is {0}", hfi.nNumberOfLinks);
                    else
                        Console.WriteLine("GetFileInformationByHandle error, code was {0}", Marshal.GetLastWin32Error());
    
                    CloseHandle(handle);
                }
                else
                    Console.WriteLine("CreateFile error, code was {0}", Marshal.GetLastWin32Error());
            }
        }
    }
    

    Thursday, April 25, 2019 3:17 PM
  • Hey thanks folks, that's real helpful.

    I owe you a beer :-)

    Friday, April 26, 2019 4:47 PM
  • Hey thanks folks, that's real helpful.

    I owe you a beer :-)


    Do you mean the question wasn't answered and there is an opportunity for more beer? :)
    Friday, April 26, 2019 5:05 PM
  • Simple example expanding on some scaffolding provided by Castorix31 -

    if (GetFileInformationByHandle(handle, ref hfi)) Console.WriteLine("Number of links is {0}", hfi.nNumberOfLinks); else Console.WriteLine("GetFileInformationByHandle error, code was {0}", Marshal.GetLastWin32Error());

    Answered! This was a great "jumping point".

    Question: Should that not be:

    if ( !GetFileInformationByHandle(handle, ref hfi))

    // If the function succeeds, the return value is nonzero (C# zero if false) Console.WriteLine("Number of links is {0}", hfi.nNumberOfLinks); else Console.WriteLine("GetFileInformationByHandle error, code was {0}", Marshal.GetLastWin32Error());


    Also:

    I GoogleStumbled a reference to having "Pack=4" in the StructLayout for BY_HANDLE_FILE_INFORMATION, is that required?

    Monday, April 29, 2019 11:50 PM