Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

13 Jun 2020

UWP的CreateFileFromApp接口

虽然有些Win32 API在UWP应用中也能使用,但是受到了一些限制。比如CreateFile2在UWP中能使用,但是不能访问除了系统允许的应用自身目录之外的其他目录中的文件。甚至是用户通过Picker选择的目录或者文件也不行。

Windows 10 version 1803引入fileapifromapp.h

为此,Windows 10 1803中引入了一些以FromApp结尾的API,在fileapifromapp.h中,可以用来扩大UWP应用的文件访问范围。比如可以访问Documents、Download之类的常用目录,如果开启了BroadFileSystemAccess访问权限的话,则可以访问所有文件。一个例子是CreateFileFromApp

APIs present on all Windows 10 devices提供了一个UWP可以访问的Win32 API的列表。

  • CopyFileFromAppW Introduced into api-ms-win-core-file-fromapp-l1-1-0.dll in 10.0.17134.
  • CreateDirectoryFromAppW Introduced into api-ms-win-core-file-fromapp-l1-1-0.dll in 10.0.17134.
  • CreateFile2FromAppW Introduced into api-ms-win-core-file-fromapp-l1-1-0.dll in 10.0.17134.
  • CreateFileFromAppW Introduced into api-ms-win-core-file-fromapp-l1-1-0.dll in 10.0.17134.
  • DeleteFileFromAppW Introduced into api-ms-win-core-file-fromapp-l1-1-0.dll in 10.0.17134.
  • FindFirstFileExFromAppW Introduced into api-ms-win-core-file-fromapp-l1-1-0.dll in 10.0.17134.
  • GetFileAttributesExFromAppW Introduced into api-ms-win-core-file-fromapp-l1-1-0.dll in 10.0.17134.
  • MoveFileFromAppW Introduced into api-ms-win-core-file-fromapp-l1-1-0.dll in 10.0.17134.
  • RemoveDirectoryFromAppW Introduced into api-ms-win-core-file-fromapp-l1-1-0.dll in 10.0.17134.
  • ReplaceFileFromAppW Introduced into api-ms-win-core-file-fromapp-l1-1-0.dll in 10.0.17134.
  • SetFileAttributesFromAppW Introduced into api-ms-win-core-file-fromapp-l1-1-0.dll in 10.0.17134.

你可能需要链接windowsapps.lib以便来访问这些API

API Redirection (Windows 10 version 1809)

为了使用FromApp这些API,需要修改应用的源代码。但是Windows 10 1809提供了一种不需要修改源代码的方法,叫做API Redirection。也就是CreateFileW 转到CreateFileFromAppW ,而不用重编译,不用修改代码。

为了达到这个目的,需要提供一个dll,来提供相应的转移调用信息。这需要DLL导出一个__RedirectionInformation__表来提供这些信息。

How can I access files (like a SQLite database) outside of my app’s folder in a UWP application?中提供相应的例子:

#include "pch.h"

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <fileapifromapp.h>

// Same signature are CreateFile2, forward it on to ...FromApp
HANDLE WINAPI CreateFile2Forwarder(LPCWSTR lpFileName, DWORD dwDesiredAccess,
  DWORD dwShareMode, DWORD dwCreationDisposition, LPCREATEFILE2_EXTENDED_PARAMETERS pCreateExParams)
{
  return CreateFile2FromAppW(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, pCreateExParams);
}

// Same signature are DeleteFileW, forward it on to ...FromApp
BOOL WINAPI DeleteFileWForwarder(LPCWSTR lpFileName)
{
  return DeleteFileFromAppW(lpFileName);
}

// Same signature are GetFileAttributesExW, forward it on to ...FromApp
BOOL WINAPI GetFileAttributesExWForwarder(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
  LPVOID lpFileInformation)
{
  return GetFileAttributesExFromAppW(lpFileName, fInfoLevelId, lpFileInformation);
}

// List of {exporting DLL}, {exported function name}, {replacement function pointer}
const REDIRECTION_FUNCTION_DESCRIPTOR RedirectedFunctions[] =
{
    { "api-ms-win-core-file-l1-2-1.dll", "CreateFile2", &CreateFile2Forwarder },
    { "api-ms-win-core-file-l1-2-1.dll", "DeleteFileW", &DeleteFileWForwarder },
    { "api-ms-win-core-file-l1-2-1.dll", "GetFileAttributesExW", &GetFileAttributesExWForwarder },
};

// The exported table, with version and size information.
extern "C" __declspec(dllexport) const REDIRECTION_DESCRIPTOR __RedirectionInformation__ =
{
    1, // version number of the structure
    ARRAYSIZE(RedirectedFunctions),
    RedirectedFunctions
};

假设上面的代码生成的dll叫做AppRedirections.dll,需要在UWP的声明文件(AppXManifest.xml)中加入相关信息:

<Package
  [other stuff]
  xmlns:uap7="http://schemas.microsoft.com/appx/manifest/uap/windows10/7" 
  IgnorableNamespaces="[other stuff] uap7">

  [more stuff...]

  [place after 'VisualElements']
  <uap7:Properties>
    <uap7:ImportRedirectionTable>AppRedirections.dll</uap7:ImportRedirectionTable>
  </uap7:Properties>
</Application>

接下来就是要把AppRedirections.dll跟你的App一起打包(如果使用Visual Studio自动打包的话,直接将DLL包含到项目即可)。

这种操作有一些限制:

  • 只能重定向App自己使用到的API,对于打包的框架所使用的API,则不会生效。
  • 对于GetProcAddress导入的函数,也不会生效。

引用一下Peter Torr - MSFT的原文

The redirections only apply to DLLs in the app's package graph (that is, the app and any Framework Packages it uses).
The redirections do not apply to functions accessed via GetProcAddress; they only work for functions directly listed in the import table.

The first limitation means that functions in system-provided DLLs will not get redirected, so you must include a version of sqlite3.dll in your app rather than rely on the system-provided one (this is the default behaviour anyway). It also means that whilst you can redirect APIs from within the VCLibs Framework Package, you cannot redirect APIs from ucrtbase.dll… this means that this technique currently doesn’t work if the app uses fopen or std::fstream etc. You can statically-link the CRT into your application to solve this problem, but it might not pass Store Certification (if you care about the Microsoft Store).

The second limitation mostly affects .NET code, since the CLR relies on LoadLibrary / GetProcAddress for resolving P/Invoke calls (although some version-adaptive C/C++ libraries use GetProcAddress as well). Note that the .NET Native compiler generates proper DLL import tables, but the normal debug builds (F5) won’t work.

另外一条评论说或许可以直接修改IAT来达成相同的目的:

Correct me if I’m wrong, but looks like you can achieve a similar effect with IAT hooking, patching sqlite3.dll’s IAT. Then you could also rely on the system-provided one. That is unless packaged apps are bound to extra limitations I’m not aware of. – Paul Feb 8 at 19:50

其他

(本篇完)

comments powered by Disqus