Win32 and Metro–CreateFile(2)

May 3, 2012

When a new Windows version comes out, I’m always curious about the new Windows API (Win32) functions that are added to the release.

With Windows 8, things get a little more complicated, as there are desktop apps and there are metro apps. Now, for every Windows API function the documentation states whether this API is valid for desktop apps only or for desktop apps and metro apps.

One classic function is CreateFile. This is one of the oldest functions – exists since the very first Windows NT version. In Windows 8, it’s marked for desktop apps only. This may be understandable, as the Windows Runtime has other ways to use files, such as the StorageFile class. However, Windows 8 has a new function called CreateFile2. This one, in contrast to CreateFile, can be used in desktop and metro apps. Here’s its prototype:

  1. HANDLE WINAPI CreateFile2(
  2.   _In_      LPCWSTR lpFileName,
  3.   _In_      DWORD dwDesiredAccess,
  4.   _In_      DWORD dwShareMode,
  5.   _In_      DWORD dwCreationDisposition,
  6.   _In_opt_  LPCREATEFILE2_EXTENDED_PARAMETERS pCreateExParams
  7. );

Here’s the prototype of CreateFile:

  1. HANDLE WINAPI CreateFile(
  2.   __in      LPCTSTR lpFileName,
  3.   __in      DWORD dwDesiredAccess,
  4.   __in      DWORD dwShareMode,
  5.   __in_opt  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  6.   __in      DWORD dwCreationDisposition,
  7.   __in      DWORD dwFlagsAndAttributes,
  8.   __in_opt  HANDLE hTemplateFile
  9. );

The first four arguments of CreateFile2 exist in both CreateFile and CreateFile2. The new extended parameters structure of CreateFile2 simply packages the “missing” arguments into an single optional structure:

  1. typedef struct _CREATEFILE2_EXTENDED_PARAMETERS {
  2.   DWORD                 dwSize;
  3.   DWORD                 dwFileAttributes;
  4.   DWORD                 dwFileFlags;
  5.   DWORD                 dwSecurityQosFlags;
  6.   LPSECURITY_ATTRIBUTES lpSecurityAttributes;
  7.   HANDLE                hTemplateFile;
  8. } CREATEFILE2_EXTENDED_PARAMETERS, *PCREATEFILE2_EXTENDED_PARAMETERS, *LPCREATEFILE2_EXTENDED_PARAMETERS;

This structure actually adds nothing to what CreateFile can specify. The dwFlagsAndAttributes of CreateFile is logically split to three groups of flags represented by dwFileAttributes, dwFileFlags and dwSecurityQosFlags in this structure for CreateFile2. Although the split is a good thing, it doesn’t explain why CreateFile is forbidden in metro style apps. Furthermore, the documentation for CreateFile2 states that in metro apps this function can only open files and directories (and not things like pipes, mailslots, consoles, etc.).

So why is CreateFile forbidden in metro apps? Perhaps it does allow opening something that CreateFile2 forbids?

I decided to try calling it from a metro app. Trying to just use CreateFile would not compile, as a set of #ifdef/#endif is placed inside the Windows headers to prevent compiling a forbidden API. This, however, can be circumvented easily. We can just create the correct prototype manually:

  1. extern "C" {
  2.     HANDLE WINAPI CreateFileW(
  3.         __in      LPCTSTR lpFileName,
  4.         __in      DWORD dwDesiredAccess,
  5.         __in      DWORD dwShareMode,
  6.         __in_opt  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  7.         __in      DWORD dwCreationDisposition,
  8.         __in      DWORD dwFlagsAndAttributes,
  9.         __in_opt  HANDLE hTemplateFile
  10.     );
  11. }

This is just the CreateFile declaration from the docs wrapped in an extern “C” declaration; this is important, otherwise the liker would complain of an unresolved external (trying to look for C++ linkage). Also, note the function is declared as CreateFileW (the Unicode version) and not simply CreateFile, as CreateFile is actually a macro, and would not have been found by the linker.

Let’s add a simple call to this CreateFileW in a blank C++ metro style application in the BlankWindow constructor:

  1. HANDLE h = ::CreateFileW(L"C:\\Users\\Pavel\\Documents\\test.txt", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, 0);
  2. if(h == INVALID_HANDLE_VALUE) {
  3.     auto err = ::GetLastError();
  4. }
  5. else
  6.     ::CloseHandle(h);

This code tries to open a text file. This fails with error code 5 (access denied), as does CreateFile2. Although I set up the manifest with access to the documents library and set up a TXT file association, it refuses to work.

There doesn’t seem to be a noticeable difference between CreateFile and CreateFile2. I don’t understand at this time why CreateFile2 exists, apart from the convenience of that extra optional structure. There’s a room for further investigation, perhaps looking at NtDll.dll to see if these call different system services (unlikely, but worth a check).

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>