activitiescache.db 是经 windows dpapi 加密的 sqlite 数据库,需先调用 cryptunprotectdata 解密为明文字节流,再通过内存或临时文件方式加载;解密必须在当前已登录用户上下文执行,payload 和 visualelements 字段为 utf-8 json 二进制数据,时间戳为 filetime 格式。

ActivitiesCache.db 是加密的 sqlite 数据库,直接用 System.Data.SQLite 打不开
windows Timeline 的活动数据存放在 %LocalAppData%Packagesmicrosoft.Windows.ShellExperienceHost_8wekyb3d8bbweLocalStateActivitiesCache.db,但它不是普通 SQLite 文件——微软用 Windows DPAPI 加密了整个数据库文件。你用常规方式打开会得到乱码或“file is encrypted or is not a database”错误。
实操建议:
- 必须先调用
CryptUnprotectData(通过 P/Invoke)解密数据库内容到内存或临时文件,再交给 SQLite 驱动读取 - 不能直接用
SQLiteConnection打开原始路径,否则报错SQL logic Error: file is encrypted or is not a database - 解密需以当前用户上下文运行(不能在服务或 SYSTEM 下执行),且用户必须已登录并解锁过系统(DPAPI 密钥绑定会话)
解密后表结构简单,但关键字段是 Activity 表里的 payload 和 visualElements
解密后的数据库里,Activity 表存所有活动记录,但主体数据藏在二进制字段中:其中 payload 是序列化的 JSON(UTF-8 编码),包含应用 ID、启动时间、用户操作类型;visualElements 是另一段 JSON,含标题、描述、图标 URI 等展示信息。
实操建议:
- 别尝试用
DataTable.Load()直接读payload字段——它返回的是byte[],需手动转Encoding.UTF8.GetString()后再JsonSerializer.Deserialize -
startTime和endTime字段是 Windows FILETIME(100-nanosecond intervals since 1601-01-01),不是 unix 时间戳,转换用DateTime.FromFileTimeUtc() - 某些活动(如 edge 浏览记录)的
payload可能为空,优先看visualElements里的displayText
权限和路径访问失败常见于 UWP 沙箱限制和路径硬编码
即使你是管理员,C# 程序默认无法访问 Microsoft.Windows.ShellExperienceHost_* 包下的 LocalState 目录,因为这是 UWP 应用专属路径,受 Package Identity 和 Capability 限制。
实操建议:
- 不要硬写完整路径,用
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)拼接,并确保程序不是以“兼容模式”或“低完整性级别”运行 - 如果提示“拒绝访问”,检查是否启用了 Windows Defender Controlled Folder access——它会拦截对
LocalState的读取 - 开发调试时,可先用 PowerShell 手动复制一份解密后的
ActivitiesCache.db到桌面再读,绕过权限问题
C# 解密 + 读取要分两步走,缺一不可
没有现成的 .NET 类封装这个流程。你得自己调 CryptUnprotectData,再喂给 SqliteConnection。跳过解密或跳过 UTF-8 解码都会导致解析失败。
简短示例逻辑:
// 先读加密文件 byte[] encrypted = File.ReadAllBytes(dbPath); // 解密(注意:pDataIn 是加密数据,pDataOut 是输出缓冲区) if (CryptUnprotectData(..., out byte[] decrypted)) { // 写入临时文件或用 MemoryStream 构造 SQLite 连接字符串 using var conn = new SqliteConnection($"Data Source=:memory:"); // ...加载 decrypted 到内存 DB 或写临时文件再连 }
容易被忽略的一点:DPAPI 解密返回的 decrypted 是完整 SQLite 文件字节流,必须原样写入磁盘或传给支持内存数据库的驱动(如 Microsoft.Data.Sqlite 的 DataSource=:memory: + LoadExtension 不行,得用 SqliteConnection + SqliteCommand 手动导入)。