我知道 UWP 应用程序可以在他们自己的 AppData 目录中使用 SQLite 数据库,但我想访问用户从另一个位置(例如他们的Downloads
目录)选择的 SQLite 数据库。我可以将数据库复制到我的应用程序目录并在那里打开它,但如果它很大,那么复制将需要很长时间,或者如果用户修改了数据库,那么我必须将其复制回来等等,我不想管理这种复杂性。
我知道 UWP 应用程序可以访问其自己的私有目录之外的文件,如果用户使用 a 选择文件FileOpenPicker
或应用程序具有此broadFileSystemAccess
功能,但这仅适用于StorageFile
对象,而不适用于SQLite 等现有库,这些库仅将文件名作为争论。我也知道我可以构建一个“完全信任”打包的 Win32 桌面应用程序,但我想构建一个在其他平台上运行的 UWP 应用程序。
UWP 中有什么新东西可以提供帮助吗?
Pet*_*SFT 10
UWP 中有几个新功能可以解决打开 SQLite 数据库的具体问题。此处使用的通用技术可以解决其他一些UWP 文件访问问题,但不是所有问题——请参阅最后的警告。
第一个特征,使这个可能是...FromApp
在Windows的API推出10版1803年这些年纪大的Win32 API等的偏差CreateFileW
,并DeleteFileW
从AppContainer内工作(安全上下文,其中UWP应用程序运行),并允许访问文件以外的应用程序的私有目录。如果您从头开始编写新代码,调用这些 API 而不是旧的 API 可确保您的代码在 UWP 上下文中“正常工作”。尽管 MSDN 还没有关于这些的很好的文档,但您可以fileapifromapp.h
在 Windows SDK的标题中找到它们。修改 SQLite 代码库以使用这些较新的 API 将使其“适合您”(请参阅下文以了解要更改的 API)。
但是,如果您不想重新编译 SQLite,或者您正在使用一个没有源代码的不同库,该怎么办?
这是使这成为可能的第二个功能派上用场的地方——API 重定向,在 Windows 10 版本 1809 中引入。此功能允许 UWP 应用程序从其自己的 DLL 中“重定向”API 导入,并改为调用不同的 API。因此,如果您的项目中有一个 DLL 尝试调用CreateFileW
并且您希望它改为调用CreateFileFromAppW
,那么现在就可以了。无需修改源代码或编译后的 DLL。
API 重定向依赖于包中的 DLL,该 DLL 导出名为__RedirectionInformation__
. 此表列出了要替换的 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>
// 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
};
Run Code Online (Sandbox Code Playgroud)
该文件从 API 集(这些是使 SQLite 工作所需的三个 API - 至少对于基本操作而言)重定向三个 API CreateFile2
、DeleteFileW
、 和。请注意,不必导出实现重定向的 API,因为没有人直接链接到它们(尽管您可以根据需要导出它们)。GetFileAttributesExW
api-ms-win-core-file-l1-2-1.dll
接下来,确保包含AppRedirections.dll
在使用 SQLite 的 UWP 应用项目中。通常,您可以从主项目“添加引用...”到重定向项目。
现在将以下条目添加/更新到您的Package.appxmanifest
文件中(或者AppXManifest.xml
如果您没有使用 Visual Studio)。您需要右键单击 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>
Run Code Online (Sandbox Code Playgroud)
这告诉 Windows 当它加载你的应用程序时,它应该首先加载AppRedirections.dll
文件,处理重定向表,然后修复它在包中的其余文件中看到的所有未来导入。请注意,如果您输入的文件名错误,或者 Windows 找不到该文件,或者它没有正确导出重定向表,您的应用程序将无法激活 (launch)。
假设你有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)
{
// StorageFile *must* be added to the future access list to ensure the Win32 APIs can grant access.
StorageApplicationPermissions::FutureAccessList->Add(pickedFile);
// now SQLite "just works"... carry on from here
int err = sqlite3_open16(pickedFile->Path->Data(), &db);
});
}
Run Code Online (Sandbox Code Playgroud)
现在,您的 UWP 应用程序应该可以使用外部 SQLite 数据库文件。类似的技术可用于其他库,但有以下注意事项(截至 2019 年 12 月):
GetProcAddress
;访问的函数。它们仅适用于导入表中直接列出的函数。第一个限制意味着系统提供的 DLL 中的函数不会被重定向,因此您必须sqlite3.dll
在应用程序中包含一个版本,而不是依赖系统提供的版本(无论如何这是默认行为)。这也意味着,虽然可以从内部重定向的APIVCLibs
框架包,你不能从重定向的API ucrtbase.dll
......这意味着,这种技术目前没有工作,如果应用程序使用fopen
或std::fstream
等可以静态链接的CRT到您的应用解决这个问题,但它可能不会通过商店认证(如果你关心微软商店)。
第二个限制主要影响 .NET 代码,因为 CLR 依赖LoadLibrary
/GetProcAddress
来解析 P/Invoke 调用(尽管一些版本自适应 C/C++ 库也使用GetProcAddress
)。请注意,.NET Native 编译器会生成正确的 DLL 导入表,但正常的调试版本 (F5) 将不起作用。
归档时间: |
|
查看次数: |
1299 次 |
最近记录: |