4

I am trying to utilize the CreateFile function to access directory information. I am recieving a win32 error code of 5 however, which means Access Denied. Please advise.

CreateFile(path, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);

This is the call being made, and as noted in the documentation the 'FILE_FLAG_BACKUP_SEMANTICS' is being used. The DLL import seems to be working fine and looks like the following:

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateFile(string filename,
                                    uint desiredAccess,
                                    uint sharedMode,
                                    IntPtr securityAttributes,
                                    uint creationDisposition,
                                    uint flagsAndAttributes,
                                    IntPtr templateFile);

Update: I need to obtain the handle to a directory so i can use GetFileInformationByHandle() and extract the unique id. This method currently works with files, it is not working with directories currently.

Update: The X for this question is i need a unique identifier of a directory that is something other than its absolute path. It needs to remain the same even if directory is moved or renamed. .NET does not provide any unique identifiers as just mentioned, it can only be accomplished by using win32

Trent Seed
  • 344
  • 5
  • 16
  • 1
    This really appears to be an XY Problem. Why are you using the API instead of the built in functionality? http://meta.stackexchange.com/a/66378/171858 – Erik Philips Apr 17 '12 at 20:15
  • 2
    I need information that is not provided by the C# File API's, it requires usage of Win32 Calls – Trent Seed Apr 17 '12 at 20:16
  • What information do you need? – Erik Philips Apr 17 '12 at 20:16
  • I need to obtain the handle to a directory so i can use GetFileInformationByHandle() and extract the unique id. This method currently works with files, it is not working with directories currently. – Trent Seed Apr 17 '12 at 20:18
  • ADS, perhaps? I'm guessing its a permissions issue, straight up. –  Apr 17 '12 at 20:18
  • @Erik, There are many cases when the built-in functionality is weak, particularly when it comes to [long file paths](http://blogs.msdn.com/b/bclteam/archive/2007/02/13/long-paths-in-net-part-1-of-3-kim-hamilton.aspx), which is something any serious file utility ought to handle. Also for those interested, [here's](http://stackoverflow.com/a/4999186/589059) more on why the not-so-intuitive `FILE_FLAG_BACKUP_SEMANTICS` is needed for getting Directory handles. – rkagerer Apr 13 '15 at 12:29

2 Answers2

5

First of all you should include manifest in your application to be sure that it runs under Administrator privileges. Then you should enable SE_BACKUP_NAME privilege using AdjustTokenPrivileges API. Then I would recommend you to use FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE flags as the sharedMode. Now you should be able to use CreateFile to open the directory handle and use GetFileInformationByHandle to get BY_HANDLE_FILE_INFORMATION.

UPDATED: Probably the following simple demo program can help you

#include <windows.h>
#include <tchar.h>

int _tmain()
{
    HANDLE hAccessToken = NULL;
    HANDLE hFile = INVALID_HANDLE_VALUE;

    __try {
        LUID luidPrivilege;
        DWORD dwErrorCode;
        BY_HANDLE_FILE_INFORMATION fiFileInfo;

        // -----------------------------------------------------
        // first of all we need anable SE_BACKUP_NAME privilege
        // -----------------------------------------------------
        if (!OpenProcessToken (GetCurrentProcess(),
                               TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                               &hAccessToken))
            __leave;

        if (LookupPrivilegeValue (NULL, SE_BACKUP_NAME, &luidPrivilege)) {
            TOKEN_PRIVILEGES tpPrivileges;
            tpPrivileges.PrivilegeCount = 1;
            tpPrivileges.Privileges[0].Luid = luidPrivilege;
            tpPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
            AdjustTokenPrivileges (hAccessToken, FALSE, &tpPrivileges, 
                                   0, NULL, NULL);
            if ((dwErrorCode = GetLastError ()) != ERROR_SUCCESS)
                __leave;
        }
        else
            __leave;

        // -----------------------------------------------------
        // now one can open directory and get 
        // -----------------------------------------------------
        hFile = CreateFile (TEXT("C:\\"),
                            0, //GENERIC_READ, 
                            0, //FILE_SHARE_READ, //FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
                            NULL,
                            OPEN_EXISTING, 
                            FILE_FLAG_BACKUP_SEMANTICS,
                            NULL);
        if (hFile == INVALID_HANDLE_VALUE)
            __leave;
        if (!GetFileInformationByHandle (hFile, &fiFileInfo))
            __leave;

        _tprintf(TEXT("VolumeSerialNumber: 0x%08X\n"), fiFileInfo.dwVolumeSerialNumber);
        _tprintf(TEXT("FileIndex: 0x%08X%08X\n"), fiFileInfo.nFileIndexHigh, fiFileInfo.nFileIndexLow);
    }
    __finally {
        if (hFile != INVALID_HANDLE_VALUE)
            CloseHandle (hFile);
        if (hAccessToken != NULL)
            CloseHandle (hAccessToken);
    }

    return 0;
}

The program opens C:\ directory and display Volume Serial Number and File Index which identify the directory on the NTFS. To make program shorter I removed all error messages (see __leave statements). Like I already mention before you should use requireAdministrator as "UAC Execution Level" (see "Manifest File" part of the Linker settings). The above code is tested and it work at me. You can reproduce the same code in C#.

Oleg
  • 220,925
  • 34
  • 403
  • 798
4

UPDATED: Probably the following simple demo program can help you...

I tried to SetFileTime() but it was wrong. I have modified so:

hFile = CreateFile(  TEXT("C:\\MyDirectory"),
// 0, //GENERIC_READ,
GENERIC_READ | GENERIC_WRITE,
// 0, //FILE_SHARE_READ, //FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);

and it is ok. Thanks. Andre.

  • 1
    This seems to be a comment on Oleg's answer, or?. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment). – Klaus Gütter Nov 21 '20 at 11:17