UWP中有几个新功能可以解决打开SQLite数据库的特定问题。这里使用的一般技术可以解决一些其他UWP文件访问问题,但不是所有问题--请参见结尾的提示。
使此功能成为可能的第一个功能是在Windows 10版本1803中引入的...FromApp
API。这些是旧Win32 API的变体,如CreateFileW
和DeleteFileW
,可以从AppContainer(UWP应用程序运行的安全上下文)中工作,并允许访问应用程序私有目录之外的文件。如果您从头开始编写新代码,请调用这些API而不是旧API,以确保您的代码将在UWP上下文中“正常工作”。虽然MSDN对它们的文档尚不完善,但您可以在Windows SDK中的fileapifromapp.h
标题中找到它们。修改SQLite代码库以使用这些更新的API将使其“正常工作”(有关要更改的API,请参见下文)。
但是,如果您不想重新编译SQLite,或者正在使用源代码不可用的不同库怎么办?
这就是第二个使这成为可能的功能-在Windows 10版本1809中引入的API重定向。此功能允许UWP应用程序“重定向”其自己的DLL的API导入,并调用其他API。因此,如果您的项目中有一个DLL尝试调用CreateFileW
,并且您希望它调用CreateFileFromAppW
,那么现在就可以了。不需要修改源代码或已编译的DLL。
API重定向依赖于软件包中导出名为__RedirectionInformation__
的特殊表的DLL。该表列出要替换的一组API以及要调用的函数。要调用的函数本身在DLL内部实现。
它是如何工作的?
首先是重定向文件。创建一个C++ UWP DLL,并将以下代码添加到主CPP文件中。我们假设此项目会产生一个名为AppRedirections.dll
的输出:
#include "pch.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <fileapifromapp.h>
HANDLE WINAPI CreateFile2Forwarder(LPCWSTR lpFileName, DWORD dwDesiredAccess,
DWORD dwShareMode, DWORD dwCreationDisposition, LPCREATEFILE2_EXTENDED_PARAMETERS pCreateExParams)
{
return CreateFile2FromAppW(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, pCreateExParams);
}
BOOL WINAPI DeleteFileWForwarder(LPCWSTR lpFileName)
{
return DeleteFileFromAppW(lpFileName);
}
BOOL WINAPI GetFileAttributesExWForwarder(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
LPVOID lpFileInformation)
{
return GetFileAttributesExFromAppW(lpFileName, fInfoLevelId, lpFileInformation);
}
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 },
};
extern "C" __declspec(dllexport) const REDIRECTION_DESCRIPTOR __RedirectionInformation__ =
{
1,
ARRAYSIZE(RedirectedFunctions),
RedirectedFunctions
};
这个文件重新定向了三个API: CreateFile2
,DeleteFileW
和GetFileAttributesExW
,从API Set api-ms-win-core-file-l1-2-1.dll
到它们的实现,这三个API至少是使SQLite基本操作得以运行所需的。请注意,实现重定向的API无需导出,因为没有人直接链接到它们(尽管您可以导出它们)。
接下来,请确保将AppRedirections.dll
包含在使用SQLite的UWP应用程序项目中。通常,您可以从主项目中“添加引用...”到重定向项目。
现在,请将以下条目添加/更新到您的Package.appxmanifest
文件中(如果您不使用Visual Studio,则为AppXManifest.xml
)。您需要右键单击并“打开方式...”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>
这告诉Windows,当加载您的应用程序时,它应首先加载AppRedirections.dll
文件,处理重定向表,然后修复其余包中所有未来导入所看到的所有导入。 请注意,如果您的文件名拼写错误,Windows找不到该文件,或者无法正确导出重定向表,则您的应用程序将无法激活(启动)。
假设您的包中有SQLite3.dll
(并且与我测试使用的版本相同),则现在您将能够使用以下代码打开SQLite数据库-请注意需要使用FutureAccessList
以“证明”您有权访问该文件:
#include <sqlite3.h>
#include <ppltasks.h>
sqlite3* db;
void OpenDatabase()
{
using namespace Windows::Storage;
using namespace Windows::Storage::Pickers;
using namespace Windows::Storage::AccessCache;
auto picker = ref new FileOpenPicker();
picker->FileTypeFilter->Append(L".db");
picker->SuggestedStartLocation = PickerLocationId::Desktop;
concurrency::create_task(picker->PickSingleFileAsync()).then([](StorageFile^ pickedFile)
{
StorageApplicationPermissions::FutureAccessList->Add(pickedFile);
int err = sqlite3_open16(pickedFile->Path->Data(), &db);
});
}
现在,您的UWP应用程序应该可以使用外部SQLite数据库文件。类似的技术也可以用于其他库,但需要注意以下限制(截至2019年12月):
- 重定向仅适用于应用程序包图中的DLL(即应用程序及其使用的任何框架包)。
- 重定向不适用于通过
GetProcAddress
访问的函数;它们仅适用于直接列在导入表中的函数。
第一个限制意味着系统提供的DLL中的函数将不会被重定向,因此您必须在应用程序中包含一个版本的 sqlite3.dll
而不是依赖于系统提供的版本(这是默认行为)。这也意味着虽然您可以从 VCLibs
框架包中重定向API,但您无法从 ucrtbase.dll
中重定向API... 这意味着如果应用程序使用 fopen
或 std::fstream
等,则当前技术无法正常工作。您可以静态链接CRT到您的应用程序中以解决此问题,但这可能无法通过商店认证(如果您关心Microsoft Store)。
第二个限制主要影响.NET代码,因为CLR依赖于 LoadLibrary
/ GetProcAddress
来解析P/Invoke调用(尽管一些版本自适应的C/C++库也使用 GetProcAddress
)。请注意,.NET Native编译器生成适当的DLL导入表,但正常的调试构建(F5)将无法工作。
sqlite3.dll
的 IAT 来实现类似的效果。然后您也可以依赖于系统提供的方法。这是除非封装应用程序受到我不知道的额外限制。 - Paul