「业务应用读不了存在桌面上的 CSV。」「换电脑之后,以前能用的导入以『找不到文件』失败。」「资源管理器看得到文件,从应用开启却出错。」── 这几年,这类来自客户的咨询已成定番。
调查之后,原因往往不是应用程序错误,而是 OneDrive 的「桌面与文档自动备份」(已知文件夹搬移,KFM)与「按需文件」。真正的桌面已搬到 C:\Users\<名称>\OneDrive\Desktop,你在那里看到的文件有一部分是没有本机内容的「占位符」。使用者和 IT 都没注意到这项变化,继续用这台电脑。
换句话说,业务应用「文件在本机磁盘上」的隐含前提,在没有人做决定的情况下,已被「文件在云端,本机只有外表」这个前提取代。本文以中小企业的信息系统人员与 Windows 应用开发者为对象,依 Microsoft Learn 第一手资料整理占位符如何运作、如何从文件属性判断状态、业务应用踩到的典型陷阱、开发端与 IT 端各自能做的事,以及接到「文件打不开」时的分流程序。
flowchart TB
accTitle: 业务应用隐含前提的替换
accDescr: 文件在本机磁盘上这个业务应用的隐含前提,在没有人做决定的情况下,已被实体在云端、本机只有外表这个前提取代
before["以往的隐含前提"] --> b1["实体在本机磁盘上"]
after["被取代后的前提"] --> a1["实体在云端"]
a1 --> a2["本机只有外表"]
a2 -.-> note["占位符"]
图 1: 「实体在本机」这个前提,在没有人做决定的情况下,已被「实体在云端,本机只有外表」取代。
1. 先讲结论
- 桌面、文档、图片可能已被 KFM 搬到
C:\Users\<名称>\OneDrive\底下。 新电脑初始设定时很容易被打开,组织也能用策略一次套用。假设固定路径的应用会在这里坏掉。1 - 目前的同步应用默认开启按需文件。 在其他设备或网页上建立的文件,会以没有本机内容的「仅联机」占位符出现。23
- 占位符的真正身分是 Cloud Files API 管理的重解析点(cldflt.sys 微型筛选器)。 对资源管理器与文件 API 都像普通文件,开启就会自动下载(水合)。4
- 状态可从文件属性判断。 FILE_ATTRIBUTE_OFFLINE、RECALL_ON_DATA_ACCESS、PINNED、UNPINNED 等是标记,attrib 命令以 O、P、U 字母显示。只检查属性不会引发下载。567
- 业务应用的典型事故是「打不开」、「变慢」、「误判属性」、「监视事件风暴」、「与同步冲突」的组合。 离线或 OneDrive 停止时水合失败,批次处理会诱发每个文件的下载。48
- 应用端的对策是「尊重占位符」。 基本做法是枚举时用属性判断、不要轻易开启,必要时用 FILE_FLAG_OPEN_NO_RECALL,不要把文件夹放在 OneDrive 底下。910
- IT 端的对策是「用固定运营」与「用策略控制」。 用「始终保留在此设备上」为业务文件夹保证实体,并用组策略 / Intune 刻意配置 KFM 与按需文件。别忘了存储感知也能「把未使用的文件退回仅联机」。1112
一句话:「资源管理器看得到的文件」与「本机磁盘上有实体的文件」已不再是同一件事。
2. 正在发生什么 ── KFM 与按需文件
2.1. 桌面可能已不是 C:\Users\<名称>\Desktop
OneDrive 同步应用有一项称为已知文件夹搬移(KFM)的功能。设置界面显示为「备份」、「备份重要文件夹」等;开启后,真正的桌面、文档、图片会被搬移(重新导向)到 OneDrive 文件夹底下。1
| 使用者看到的位置 | KFM 前的实际路径 | KFM 后的实际路径 |
|---|---|---|
| 桌面 | C:\Users\taro\Desktop |
C:\Users\taro\OneDrive\Desktop |
| 文档 | C:\Users\taro\Documents |
C:\Users\taro\OneDrive\Documents |
| 图片 | C:\Users\taro\Pictures |
C:\Users\taro\OneDrive\Pictures |
新电脑初始设定(OOBE)以 Microsoft 帐户或公司帐户登入时,文件夹备份常被当成默认提案,照著往下走就会开启。组织也能用「以静默方式将 Windows 已知文件夹移到 OneDrive」策略(KFMSilentOptIn)不问使用者就一次套用。111
flowchart TB
accTitle: KFM 被开启的两条路径
accDescr: 新电脑初始设定以帐户登入时,文件夹备份会被当成默认提案,照著往下走就会开启;在组织里 KFMSilentOptIn 策略会不问用户就一次套用
oobe["新电脑的初始设定"] --> signin["以帐户登入"]
signin --> prompt["默认提出备份"]
prompt --> on1["照著往下走就开启"]
org["组织策略"] --> silent["KFMSilentOptIn"]
silent --> on2["不问使用者就一次套用"]
on1 --> kfm["KFM 开启"]
on2 --> kfm
图 2: KFM 会在没人注意的情况下开启,来源是初始设定的默认提案,或组织的无讯息套用策略。
尴尬之处在于 资源管理器的外表几乎不变。外壳已知文件夹 API(SHGetKnownFolderPath 与 .NET 的 Environment.GetFolderPath)会回传搬移后的正确路径,所以守规矩的应用会继续运作。坏掉的是 在配置文件或代码里写死 C:\Users\%USERNAME%\Desktop 这类固定路径的应用。换电脑后导入以「找不到文件」失败的典型模式就是这个。
flowchart TB
accTitle: KFM 之后应用如何解析路径
accDescr: KFM 把真正的桌面等文件夹搬到 OneDrive 底下后,使用已知文件夹 API 的应用会以搬移后的正确路径继续运作,但写死固定路径的应用会找不到文件
kfm["KFM 已开启"] --> move["真正的桌面等搬到 OneDrive 底下"]
move --> how{"应用如何解析路径?"}
how -->|已知文件夹 API| ok["取得搬移后的正确路径并继续运作"]
how -->|写死的固定路径| ng["找不到文件"]
图 3: KFM 之后,使用已知文件夹 API 的应用会继续运作,但写死固定路径的应用会在这里坏掉。
2.2. 按需文件 ── 看得到,但没有实体
另一条线索是按需文件。在开启的环境里,OneDrive 上的每个文件都在资源管理器里看得到,但 内容要等到文件被开启才下载。这项功能在目前的同步应用里默认开启,Microsoft 也建议维持开启。23
状态可从资源管理器的状态图示分辨。13
| 图示 | 状态 | 本机内容 |
|---|---|---|
| 云朵标记 | 仅联机 | 无(只有占位符) |
| 白底勾选 | 本机可用 | 有(之后可能被自动释放) |
| 绿底白勾 | 始终保留在此设备上(固定) | 有(不在自动释放范围) |
这里重要的是中间状态。开过一次、已有本机内容的文件,可能因使用者的「释放空间」或稍后会谈到的存储感知 再变回仅联机。这是「上个月还能用」这类难以重现故障的原因之一。312
stateDiagram-v2
accTitle: 按需文件的三种状态与转换
accDescr: 仅联机的文件开启后会变成本机可用,但释放空间或存储感知可把它退回仅联机,只有固定的文件不在自动释放范围内
s1: 仅联机(云朵标记)
s2: 本机可用
s3: 固定(始终保留在此设备上)
s1 --> s2: 开启(水合)
s2 --> s1: 释放空间
s2 --> s1: 存储感知
s1 --> s3: 始终保留在此设备上
s2 --> s3: 始终保留在此设备上
s3 --> s2: 取消固定
图 4: 按需文件的三种状态。「本机可用」可能自动退回仅联机;固定不在该范围内。
3. 占位符的真正身分 ── Cloud Files API 与重解析点
按需文件实作在 Windows 10 版本 1709 引入的 OS 机制 Cloud Files API 之上。文件系统端的工作单位是名为 cldflt.sys 的文件系统微型筛选器(服务名称 CldFlt,「Windows Cloud Files Filter Driver」),OneDrive 是使用此 API 的「同步提供者」之一。47
占位符在技术上是 重解析点。文件系统上只有档名、大小、时间戳等元数据(约 1KB),没有内容数据。应用开启文件并读取时,微型筛选器侦测到要求,指示同步提供者传输数据,等下载完成后读取才继续。这次获取称为 水合(hydration);丢掉本机内容、回到占位符称为 脱水(dehydration)。4
sequenceDiagram
accTitle: 开启占位符时的水合
accDescr: 应用开启占位符并读取时,cldflt.sys 微型筛选器侦测要求,指示同步提供者传输数据,等下载完成后读取才继续
participant app as 业务应用
participant flt as cldflt.sys 微型筛选器
participant sync as 同步提供者
app->>flt: 开启并读取的要求
flt->>sync: 指示数据传输
sync-->>flt: 下载完成
flt-->>app: 读取继续
图 5: 占位符的读取,是在微型筛选器让同步提供者抓回数据之后才继续。
听到「重解析点」会担心与「侦测到重解析点就特别处理」的既有代码相容,但为了相容,Cloud Files API 对同步引擎与 %systemroot% 底下以外的所有人隐藏它是重解析点这件事。对普通应用来说,它看起来像「只是开启稍微慢一点的普通文件」。这种彻底的透明既方便,也是「应用在没注意到的情况下被打破前提」的原因。4 重解析点本身的机制在「NTFS 内部结构」说明。
flowchart TB
accTitle: 隐藏重解析点,以及看起来的差异
accDescr: 占位符的真正身分是重解析点,但 Cloud Files API 对同步引擎以外的程序隐藏此事,因此对普通应用看起来只是开启稍微慢一点的普通文件
ph["占位符(重解析点)"] --> who{"是哪个程序开启的?"}
who -->|同步引擎等| raw["可见为重解析点"]
who -->|其他应用| plain["看起来像普通文件"]
plain -.-> note["看起来只是开启稍慢"]
图 6: 它是重解析点这件事对同步引擎以外的所有人隐藏,对普通应用看起来像普通文件。
在资源管理器属性中,占位符的特征是 「大小」显示原始大小,而「磁盘上的大小」几乎是 0。「有大小,所以一定有实体」这个假设在这里不成立。
flowchart TB
accTitle: 占位符在属性中的样子
accDescr: 在资源管理器属性中,占位符的大小显示原始大小,磁盘上的大小几乎是 0,因此有大小就一定有实体的假设不成立
prop["占位符内容"] --> size["大小是原始大小"]
prop --> disk["磁盘上的大小几乎是 0"]
size -.-> trap["一定有实体的假设"]
disk -.-> truth["没有本机内容"]
图 7: 占位符的「大小」显示原始大小,而「磁盘上的大小」几乎是 0。
4. 文件属性会告诉你状态
占位符状态以普通文件属性公开。主要如下。5
| 属性 | 值 | 意义 |
|---|---|---|
| FILE_ATTRIBUTE_OFFLINE | 0x00001000 | 数据无法立即使用(分层式存储管理的传统属性) |
| FILE_ATTRIBUTE_RECALL_ON_OPEN | 0x00040000 | 没有实体本机内容。只出现在目录枚举结果 |
| FILE_ATTRIBUTE_PINNED | 0x00080000 | 使用者意图「一律留在本机」(固定) |
| FILE_ATTRIBUTE_UNPINNED | 0x00100000 | 不必保留本机内容(改成仅联机的意图) |
| FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS | 0x00400000 | 部分或全部内容不在本机。读取会从远端获取 |
命令提示字元的 attrib 命令可用单一字母显示与设定这些属性。O 是离线属性,P 是固定,U 是取消固定。6 与 OneDrive 按需文件状态的对策,Microsoft 文件整理如下。7
| 按需文件状态 | 属性 | 设定命令 |
|---|---|---|
| 一律可用(固定) | Pinned(显示 P) | attrib +p <path> |
| 本机可用 | 既非 P 也非 U | attrib -p <path> |
| 仅联机 | Unpinned(显示 U) | attrib +u <path> |
有一点要注意。切换状态有顺序。 想把仅联机(U)的文件变成「本机可用」时,只跑 -p 会留下 U,实体不会被抓回来。Microsoft 文件也示范先做 +p(一律可用)下载实体,再做 -p 的程序。7 在必须可靠切换既有状态的脚本里,较安全的做法是像 attrib +p -u 这样 同时清掉相反属性。
flowchart TB
accTitle: 从仅联机切到本机可用的顺序
accDescr: 对仅联机的文件只运行 attrib -p 会留下 U 属性且不会抓回实体;需要先用 attrib +p 下载实体再做 -p
u["仅联机(U)"] -->|只做 attrib -p| stay["维持 U;实体未被抓回"]
u -->|attrib +p| pin["固定(下载实体)"]
pin -->|attrib -p| local["本机可用"]
图 8: 从仅联机切换,需要先用 +p 抓回实体,再做 -p 的顺序。
PowerShell 判断的例子。只看属性不会引发水合,因此可放心用于调查与批次检查。
function Test-CloudPlaceholder {
param([Parameter(Mandatory)][string]$Path)
$value = [int](Get-Item -LiteralPath $Path -Force).Attributes
[pscustomobject]@{
Path = $Path
Offline = ($value -band 0x00001000) -ne 0 # FILE_ATTRIBUTE_OFFLINE
RecallOnDataAccess = ($value -band 0x00400000) -ne 0 # 内容并非全部在本机
Pinned = ($value -band 0x00080000) -ne 0 # 始终保留在此设备上
Unpinned = ($value -band 0x00100000) -ne 0 # 仅联机
}
}
# 批次检查文件文件夹底下的 CSV(不会下载内容)。
# 用已知文件夹 API 解析路径。把显示名称
# "Documents" 写死,可能因实际文件夹名称
# (Documents 对上本地化名称)与 KFM 配置
# 而变成不存在的路径
Get-ChildItem ([Environment]::GetFolderPath('MyDocuments')) -Recurse -Filter *.csv |
ForEach-Object { Test-CloudPlaceholder $_.FullName } |
Where-Object RecallOnDataAccess |
Format-Table -AutoSize
转成 [int] 是因为 .NET 的 FileAttributes 枚举没有定义 RECALL_ON_DATA_ACCESS 这类名称。对数值做位元运算就能顺利判断。
5. 业务应用踩到的陷阱
这是本题。占位符的透明大多数时候很方便,但与典型业务应用处理模式叠在一起,会以以下六种形状浮现。
5.1. 开启就立刻开始下载 ── 离线时「打不开」
开启仅联机的文件会当场开始水合。线上、文件又小,快到你不会注意到,但 当 OneDrive 停止、登出或暂停、网络不稳、或文件很大时,就变成「文件存在但打不开」。错误可能以 ERROR_CLOUD_FILE_PROVIDER_NOT_RUNNING(0x8007016A,”The cloud file provider is not running”)这类云端文件系列代码回来,也可能在应用端被观察成逾时。8
更进一步的陷阱是,相当于 File.Exists() 的存在检查,以及取得属性或大小,都会成功。你会得到本机磁盘直觉解释不了的错误模式:「存在检查过了,读取却失败」。
flowchart TB
accTitle: 访问仅联机文件时的分支
accDescr: 存在检查与取得属性、大小会成功,但读取内容会开始水合;若 OneDrive 在运行且网络正常,下载后可读,否则以 0x8007016A 等错误或逾时失败
check["存在检查,或取得属性或大小"] --> ok1["成功"]
open["读取内容"] --> hyd["开始水合"]
hyd --> cond{"OneDrive 在运行且网络正常?"}
cond -->|是| read["下载后可读"]
cond -->|否| err["0x8007016A 等错误,或逾时"]
图 9: 存在检查可以成功,读取却失败。成败取决于 OneDrive 是否在运行,以及网络。
5.2. 批次处理诱发每个文件的下载
把读取文件夹内每个文件的批次、哈希计算、全文搜寻或自制备份对准 OneDrive 底下的树,你碰到的每个文件都会被诱发水合。对数 GB 的文件夹,处理会异常变慢,下载也会填满磁盘,低容量电脑上空间不足会引出另一种故障。按需文件原本要省下的容量,一次全扫描就没了。
此外,若应用在没有明确使用者操作的情况下引发水合,Windows 可能显示Toast并让使用者选择封锁。一旦封锁,该应用之后的下载会持续失败(可在设定的「自动下载文件」解除)。这是「只有某台电脑导入失败」的原因之一。4
flowchart TB
accTitle: 批次处理如何诱发每个文件的下载
accDescr: OneDrive 底下的批次会诱发碰到的每个文件水合,造成处理延迟与磁盘压力,若使用者在Toast上封锁,之后下载会持续失败
scan["OneDrive 底下的批次"] --> touch["碰到的每个文件都水合"]
touch --> cost["处理延迟与磁盘压力"]
touch --> toast["可能出现Toast"]
toast --> block{"使用者封锁了吗?"}
block -->|是| fail["之后下载持续失败"]
block -->|否| cont["下载继续"]
图 10: 批次会诱发每个文件水合,若在Toast上被封锁,之后失败会持续。
5.3. 没预期这些属性的代码的误动作
不知道 FILE_ATTRIBUTE_OFFLINE 或 RECALL_ON_DATA_ACCESS 的代码,会在意想不到的地方误动作。
- 属性用完全相等测试(
attributes == FileAttributes.Archive等),占位符被当成「非预期文件」排除或当错误处理 - 备份或同步工具的排除判断把 OFFLINE 属性解读成「已送到磁带」而略过(或反过来,把本该排除的每个文件都抓回来)
- 只读检查或封存位元操作破坏属性组合
flowchart TB
accTitle: 没预期属性的代码的误动作模式
accDescr: 不知道占位符属性的代码,会因完全相等属性测试而排除或当错误、因误解 OFFLINE 而略过或全抓、或因属性操作破坏组合而误动作
code["没预期属性的代码"] --> m1["完全相等测试"]
code --> m2["误解 OFFLINE"]
code --> m3["属性操作破坏组合"]
m1 --> r1["被当成非预期而排除或出错"]
m2 --> r2["略过,或全部抓回"]
图 11: 不知道 OFFLINE 或 RECALL 系列属性的代码,会以排除、错误略过或属性破坏的方式误动作。
Microsoft 给微型筛选器开发者的指引明白写道:不应对带有 RECALL_ON_DATA_ACCESS 的文件发出轻率的读写。文件针对核心驱动程序,但「碰到带此属性的文件内容 = 发生获取成本」这个原则,原封不动适用于使用者模式应用。10
5.4. FileSystemWatcher 与同步的交互作用
用 FileSystemWatcher 监视 OneDrive 底下的文件夹,你得到的不只是使用者操作,还有 同步应用活动带来的大量事件。其他设备的变更每次同步、水合或脱水每次改变属性或大小,都可能发出 Changed 事件。再者,把监视并导入的结果写回同一文件夹的设计,会在写入 → 上传 → 属性更新 → 另一个事件的回圈里变成「变更通知风暴」。事件节流与实体检查的设计见「FileSystemWatcher 的使用方法与注意事项」,但在 OneDrive 底下,这项需求又高一阶。
flowchart TB
accTitle: 监视并写回造成的变更通知回圈
accDescr: 若收到变更事件的监视应用把导入结果写回同一文件夹,同步应用的上传与属性更新会再发出事件,形成变更通知风暴的回圈
ev["变更事件"] --> proc["监视应用导入"]
proc --> write["写回同一文件夹"]
write --> up["同步应用上传"]
up --> attr["属性或大小被更新"]
attr --> ev
sync["其他设备变更的同步"] -.-> ev
图 12: 把导入结果写回同一文件夹,会变成同步应用活动再产生事件的回圈。
5.5. 独占锁期间的同步冲突,以及「副本」文件
业务应用以独占锁开启文件期间,同步应用无法上传或更新该文件。把长时间持锁的应用(Access .accdb、自制格式数据档、日志文件等)放在 OneDrive 底下,同步错误会变成常态。反过来说,同一文件在多台电脑上编辑时,同步应用会试著两边都留,并产生带电脑名称的重复档,或「— 副本」这类冲突副本。假设「一个文件夹、一个文件」的导入会在这份重复上误动作。锁定设计的基础见「文件集成互斥控制基础知识」。
flowchart TB
accTitle: 独占锁与多机编辑造成的同步问题
accDescr: 应用以独占锁开启文件期间同步应用无法更新,同步错误变成常态;同一文件在多台电脑编辑会产生冲突副本,一个文件夹一个文件的假设崩解
lock["应用以独占锁开启"] --> nosync["无法同步;同步错误成常态"]
multi["同一文件在多台电脑编辑"] --> conflict["产生冲突副本"]
conflict --> dup["带电脑名称或副本的重复"]
dup --> bad["一个文件夹一个文件的假设崩解"]
图 13: 独占锁让同步错误成常态,多台电脑编辑则因冲突副本引出误动作。
5.6. 杀毒与搜索索引器诱发水合
读取文件内容的不只是业务应用。杀毒软件的全盘扫描与搜索索引器,只要碰到占位符的内容也会诱发水合。Microsoft Defender 等产品在按需扫描时会略过带 RECALL_ON_DATA_ACCESS 属性的文件,但那是产品端的对策,不能假设每个安全产品都会同样小心。若看到「每晚扫描时网络与磁盘都被打满」或「本该仅联机的文件到早上已全部落地」这类症状,就怀疑这条线。14
flowchart TB
accTitle: 安全产品或搜索索引器诱发的水合
accDescr: 全盘扫描或搜索索引器碰到占位符内容时,尊重 RECALL 属性的产品会略过,不尊重的产品会让每个文件水合,造成夜间频宽压力或早晨落地
av["全盘扫描或搜索索引器"] --> care{"尊重 RECALL 属性吗?"}
care -->|尊重的产品| skip["略过占位符"]
care -->|不尊重的产品| hyd["碰到内容并水合"]
hyd --> sym1["夜间频宽与磁盘被打满"]
hyd --> sym2["到早上文件已全部落地"]
图 14: 不尊重属性的扫描会诱发每个文件水合,并以夜间负载或早晨落地显现。
6. 应用开发端的对策 ── 尊重占位符
作为开发者的基本方针,是把占位符当成 「有获取成本的文件」而不是「坏掉的文件」。
- 枚举时用属性判断,不要轻易开启。 文件夹扫描时,先从属性(第 4 章的判断)确认是否仅联机,只开启你需要内容的文件。对「缺了也不致命」的处理 ── 日志收集、哈希计算、预览生成 ── 给略过占位符的选项。
// 把 .NET 的 FileAttributes 未定义的值用数字定义
const FileAttributes RecallOnDataAccess = (FileAttributes)0x00400000;
const FileAttributes RecallOnOpen = (FileAttributes)0x00040000;
static bool IsCloudPlaceholder(FileAttributes attributes) =>
(attributes & (RecallOnDataAccess | RecallOnOpen | FileAttributes.Offline)) != 0;
foreach (var file in new DirectoryInfo(watchFolder).EnumerateFiles("*.csv"))
{
if (IsCloudPlaceholder(file.Attributes))
{
log.Warn($"{file.Name} 仅联机;这次略过");
continue;
}
Import(file.FullName);
}
flowchart TB
accTitle: 枚举时用属性判断再开启的路径
accDescr: 文件夹扫描时先在枚举确认属性;若是占位符就略过并留警告记录,只对其他文件运行导入,以免轻率水合
enum["枚举时确认属性"] --> ph{"占位符?"}
ph -->|是| skip["略过并留警告记录"]
ph -->|否| imp["运行导入"]
skip -.-> note["只开启需要内容的文件的方针"]
图 15: 枚举时用属性判断,略过占位符而不开启,以免轻率水合。
- 注意 FILE_FLAG_OPEN_NO_RECALL 不是「不要下载」的保证。 在 CreateFile 指定此标志,可表示「取得的数据应留在远端、不要写回本机存储」的意图。不过它只是 不要让取得的数据驻留本机的标志;若你读内容,数据传输本身仍会发生。若要避开频宽与延迟本身,只用属性、大小、时间戳结束 ── 不要要求读取访问(以访问权限 0 开启,或用枚举结果的元数据)。那才最安全。9
flowchart TB
accTitle: FILE_FLAG_OPEN_NO_RECALL 的效果与界限
accDescr: FILE_FLAG_OPEN_NO_RECALL 是不让取得的数据驻留本机的标志;若读内容,数据传输本身仍会发生,因此若要避开传输,最安全是只用属性等元数据结束
flag["以 NO_RECALL 标志开启"] --> read["读取内容"]
read --> transfer["发生数据传输"]
transfer --> nolocal["不会驻留本机"]
meta["只用元数据结束"] --> safe["不发生传输;最安全"]
图 16: FILE_FLAG_OPEN_NO_RECALL 只避免驻留本机;若要避开传输本身,只用元数据结束。
- 在错误讯息里写「这在 OneDrive 底下」。 读取失败时,只要确认目标路径是否在
%OneDrive%底下并写进讯息,现场与帮助台的分流时间就会大幅缩短。若侦测到 0x8007016A 这类云端文件系列错误,理想是告诉使用者「请检查 OneDrive 的状态」。 - 不要把应用的文件夹放在 OneDrive 底下。 在 KFM 环境,「文件」也在 OneDrive 底下。把应用的设定、数据库、工作档放在
%ProgramData%或%LocalAppData%,不要把桌面或文件选成默认存储位置或默认导入文件夹。决定放哪里,见「Windows应用的数据存储位置怎么选」。 - 决定使用者选了 OneDrive 底下位置时的行为。 对让使用者选择存储位置的应用,事先在规格里纳入设计决策,例如所选路径在 OneDrive 底下(
OneDrive/OneDriveCommercial环境变量的路径底下)时警告,或只拒绝放置锁定档或数据库。
7. IT 端的对策 ── 用固定与策略控制
从 IT 的位置,务实的运营不是「整段关掉按需文件」,而是 只在业务需要的地方保证实体。
- 固定业务应用会读的文件夹。 从资源管理器右键选单选「始终保留在此设备上」,或在映像脚本运行
attrib +p -u <folder> /s /d(同时指定-u,才能把已是仅联机的文件可靠切到固定)。固定的文件在本机保证有实体,也不在稍后谈到的自动转成仅联机的范围内。72 - 刻意配置 KFM 与按需文件,而不是「注意到时已经开著」。 主要策略(组策略 / Intune)如下。111
| 目的 | 策略(注册表值) | 效果 |
|---|---|---|
| 控制按需文件 | Use OneDrive Files On-Demand (FilesOnDemandEnabled) | 开:新使用者默认仅联机。关:传统完整同步 |
| 一次套用 KFM | Silently move Windows known folders to OneDrive (KFMSilentOptIn) | 不经使用者操作就搬移桌面等 |
| 禁止 KFM | Prevent users from moving their Windows known folders to OneDrive (KFMBlockOptIn) | 禁止搬移已知文件夹 |
| 禁止关掉 KFM | Prevent users from redirecting their Windows known folders to their PC (KFMBlockOptOut) | 禁止使用者关掉 |
| 降低小组网站容量 | Convert synced team site files to online-only (DehydrateSyncedTeamSites) | 把已同步的小组网站改成仅联机(注意它朝实体消失的方向作用) |
- 了解存储感知如何移动。 存储感知有一项功能,把若干天未开启的云端文件自动退回仅联机,天数可用策略(ConfigStorageSenseCloudContentDehydrationThreshold)设定。默认是 0(不自动退回),但若使用者从设置界面开启,或组织为低容量设备设定了,则「上周开过的文件变回云朵图示」会当正常行为发生。固定的文件不在范围内,所以「固定业务文件夹」在这里也有效。122
flowchart TB
accTitle: 存储感知自动转成仅联机的分支
accDescr: 在存储感知的自动释放中,固定的文件不在范围内且实体保留;未固定且若干天未开启的文件会被退回仅联机
ss["存储感知自动释放"] --> pin{"已固定?"}
pin -->|是| stay["不在范围内;实体保留"]
pin -->|否| old{"若干天未开启?"}
old -->|是| dehyd["退回仅联机"]
old -->|否| keep["实体保留"]
ss -.-> def["默认 0 不自动退回"]
图 17: 存储感知把若干天未开启的文件退回仅联机,但固定不在范围内。
- 停用按需文件前先估计影响。 停用 FilesOnDemandEnabled 会变成传统完整下载同步,但磁盘消耗与第一次同步的频宽负载会跳升。Microsoft 建议维持开启,停用应视为确认「目标使用者数据量小」与「磁盘有余裕」之后的有限措施。112
- 写进支援程序。 把下一章的分流程序放进「桌面上的文件打不开」问询模板,即使经办人换了,应对质量也能维持。
8. 分流程序 ── 接到「文件打不开」时
接下咨询时,由上往下确认。
| # | 要确认的事 | 方法 | 你会知道的事 |
|---|---|---|---|
| 1 | 路径在 OneDrive 底下吗? | 用 echo %OneDrive% 确认同步根并对上目标路径。也在资源管理器地址栏确认「桌面」的实际路径 |
KFM / OneDrive 是否介入 |
| 2 | 文件的状态 | 用 attrib <path> 确认 U(仅联机)、P(固定)、O。也看内容的「磁盘上的大小」 |
实体是否在本机,或是占位符 |
| 3 | OneDrive 是否在运行 | 任务栏图示(已登入、暂停、错误)、Get-Process OneDrive |
水合是否可能。0x8007016A 典型是停止或配置错误8 |
| 4 | 网络 | 公司代理、频宽、到 OneDrive 服务的可达性 | 下载本身是否可能 |
| 5 | 磁盘可用空间 | 目标卷的可用空间。容量低时也有 OneDrive 阻挡下载的策略 | 水合失败的另一因素 |
| 6 | 失败纪录 | 记下应用的错误码与发生时间,对上同步应用的错误显示 | 是应用端问题还是 OneDrive 端问题 |
权宜办法是对目标文件夹按右键,选「始终保留在此设备上」(或 attrib +p /s /d)。这样实体会在本机排齐,业务可以恢复。在此之上,再决定本质原因在应用端(第 6 章)还是 IT 端(第 7 章),作为永久对策。
flowchart TB
accTitle: 从权宜到永久对策的路径
accDescr: 权宜办法是把目标文件夹设成始终保留在此设备上,实体在本机排齐后业务可恢复;在此之上决定本质原因在应用端还是 IT 端,再进入永久对策
aid["权宜固定"] --> restore["实体在本机排齐"]
restore --> resume["业务恢复"]
resume --> judge{"本质原因在哪?"}
judge -->|应用端| dev["到第 6 章的对策"]
judge -->|IT 端| ops["到第 7 章的对策"]
图 18: 权宜是固定、排齐实体、恢复业务;永久对策在决定是应用端还是 IT 端之后进行。
若确认到这里,「路径不在 OneDrive 底下」且「也不是占位符」,就往共享文件夹或路径长度等其他定番原因走。「网络磁盘机与 UNC 路径的陷阱」与「MAX_PATH 与 Windows 路径・文件名称的陷阱」是接下来的地图。
9. 总结
- KFM 可能已把真正的桌面、文档、图片搬到
C:\Users\<名称>\OneDrive\底下。假设固定路径的应用会在这里坏掉。用已知文件夹 API 解析是第一步。 - 按需文件默认开启,没有本机内容的占位符理所当然存在。占位符是 Cloud Files API(cldflt.sys)的重解析点,开启就会自动水合。
- 状态可从文件属性(OFFLINE / RECALL_ON_DATA_ACCESS / PINNED / UNPINNED)判断,在 attrib 显示为 O、P、U。只检查属性不会引发下载。
- 业务应用事故呈现为离线时水合失败、批次处理的完整下载、没预期属性的代码、FileSystemWatcher 与同步的交互、独占锁与同步的冲突,以及安全产品诱发的水合。
- 应用端的基本是「用属性判断、不要轻易开启」、「不要把文件夹放在 OneDrive 底下」、「出错时说它在 OneDrive 底下」。
- IT 端用「固定业务文件夹」与「KFM、按需文件、存储感知的策略控制」做出预期状态。
- 分流可依路径 → attrib → OneDrive 运行 → 网络 → 可用空间 → 纪录的顺序机械地走完。
下次接到「文件在那里但打不开」,先问这个。
那个文件真的在本机磁盘上吗?还是只有云端的外表坐在那里?
相关文章
- Windows I/O 的深层(第5回) ── NTFS 的内部结构:从 MFT 理解文件系统
- FileSystemWatcher 实务指南:应对遗漏通知与重复通知
- 网络驱动器与 UNC 路径的陷阱 ── 业务应用中处理文件服务器(共享文件夹)的实务
- 文件集成互斥控制基础知识 - 文件锁与原子 claim 的最佳实践
- Windows应用的数据存储位置怎么选 ── SQLite / JSON / 注册表 / Access 判断表
- MAX_PATH 与 Windows 路径・文件名的陷阱 ── 260 字符限制、保留名称、末尾句点、大小写
相关咨询领域
小村软件有限公司处理与 OneDrive、云端存储有关的业务应用故障调查 ── 「以前能用的导入换电脑后就不能用」、「只有某台电脑打不开文件」── 假设占位符的文件处理与监视处理的设计与修正,以及 KFM / 按需文件环境下存储位置设计的审查。从厘清症状开始也没关系 ── 欢迎联系。
参考链接
-
Microsoft Learn, Redirect and move Windows known folders to OneDrive. KFM 把桌面、文件、图片搬到 OneDrive 底下,以及提示、无讯息套用、禁止关闭、禁止搬移等原则。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Recommended sync app configuration. 按需文件默认开启且建议维持开启,以及存储感知会清理「未固定的本机可用文件」。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Support, Save disk space with OneDrive Files On-Demand for Windows. 按需文件的三种状态,以及「始终保留在此设备上」与「释放空间」操作。 ↩ ↩2 ↩3
-
Microsoft Learn, Build a Cloud Sync Engine that Supports Placeholder Files. Cloud Files API 概观、占位符只持有约 1KB 元数据且开启会自动水合、重解析点对同步引擎与 %systemroot% 以外的程序隐藏,以及背景水合的Toast与封锁。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, File Attribute Constants. FILE_ATTRIBUTE_OFFLINE、RECALL_ON_OPEN、RECALL_ON_DATA_ACCESS、PINNED、UNPINNED 的定义与值。 ↩ ↩2
-
Microsoft Learn, attrib. attrib 命令语法,以及包含 O(离线)、P(固定)、U(取消固定)的属性标志。 ↩ ↩2
-
Microsoft Learn, Query and set Files On-Demand states in Windows. 用 attrib 确认按需文件状态,以及用 +p、-p、+u 设定,还有 CldFlt 服务。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Error 0x8007016a when copying files in OneDrive. 错误 0x8007016A “The cloud file provider is not running” 在 OneDrive 配置错误或停止时发生,以及解决步骤。 ↩ ↩2 ↩3
-
Microsoft Learn, CreateFileW function (fileapi.h). FILE_FLAG_OPEN_NO_RECALL 是表示「要求的数据应留在远端、不要传回本机存储」的标志(不阻止取得数据本身),以及以访问权限 0 开启来取得属性。 ↩ ↩2
-
Microsoft Learn, Handling placeholders. 占位符应设定 FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS,以及对带此属性的文件轻率读写会招来不必要的水合或数据损毁。 ↩ ↩2
-
Microsoft Learn, IT Admins - Use OneDrive policies to control sync settings. 用 GPO/Intune 设定 OneDrive 同步应用的策略,包括 FilesOnDemandEnabled、KFMSilentOptIn、KFMBlockOptIn、KFMBlockOptOut、DehydrateSyncedTeamSites。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Policy CSP - Storage. 存储感知可把若干天未开启的云端文件改成仅联机、默认 0(不自动退回),以及 0–365 天的设定。 ↩ ↩2 ↩3
-
Microsoft Support, What do the OneDrive icons mean?. 资源管理器显示的云朵与勾选等状态图示的意义。 ↩
-
Microsoft Learn, Plan for an Azure File Sync deployment. 杀毒扫描可能造成带 RECALL_ON_DATA_ACCESS 属性的文件被召回,以及 Microsoft Defender 等产品在按需扫描时会略过带此属性的文件。 ↩
相关文章
共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。
多线程实务最佳实践 .NET 篇 ── 在增加线程之前应先确定的事
针对 .NET/C# 整理「立了线程之后,偶尔崩溃・卡死」的防范设计准则。内容涵盖不自行创建线程而改用 Task、减少共享可变状态、锁的纪律、基于 CancellationToken 的停止设计,直至 UI 线程的处理方式。
卷影复制服务(VSS)的原理与实务 ── 使用中文件为何能够备份
使用中的文件明明会因共享冲突而无法复制,备份软件为什么却能正常备份?本文将解说卷影复制服务(VSS)中请求者・编写器・提供程序的角色分工、写时复制的原理、vssadmin 的实务操作,以及差异区域的陷阱。
Windows证书存储实务指南 ── 应该放入用户存储还是计算机存储
客户端证书究竟应该放入用户存储还是计算机存储?本文从 certmgr.msc 与 certlm.msc 的区别、私钥的权限授予,到 PowerShell 的到期日盘点,系统性地梳理证书相关的常见事故与对策,是一份实务指南。
Windows 防火墙与业务应用 ── 入站规则要通过安装程序注册
「开发机上正常运行,但在客户现场却无法通信」的常见原因就是 Windows 防火墙。本文讲解入站默认阻止与网络配置文件、不能把生产环境交给通知对话框处理的原因,以及通过安装程序注册入站规则的方法与排查步骤。
从睡眠恢复后损坏的应用 ── Windows 电源事件机制与扛得住恢复的业务应用
打开笔记本后业务应用的连接全断了——原因是设计从未考虑睡眠。本文根据一手资料整理 WM_POWERBROADCAST 通知流程、Modern Standby 行为、断开/重连设计、睡眠抑制与调查命令。
相关主题
与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。
Windows 技术主题
汇整 KomuraSoft LLC 关于 Windows 开发、故障调查与既有资产活用文章的主题中心。
故障调查 & 长期运行故障
整理间歇性故障、通信诊断、长期运行崩溃、失败路径测试基础的主题页面。
与本主题相关的服务
本文与以下服务页面相关联,欢迎从最接近的入口查看。
Windows 应用程序开发
支持包含常驻处理、设备联动、运行日志与可维护结构的 Windows 桌面应用程序。
故障调查 & 根本原因分析
调查难以复现的故障、长时间运行后的问题、内存泄漏、通信停滞等棘手的生产环境问题。
常见问题
汇总了咨询这一主题时常见的问题。
- 业务应用说「找不到文件」,读不了放在桌面上的 CSV。为什么?
- 多数情况是桌面文件夹本身已被 OneDrive 的已知文件夹搬移(KFM)移到 C:\Users\<使用者名称>\OneDrive\Desktop,或文件已变成仅联机的占位符。假设固定路径如 C:\Users\<使用者名称>\Desktop 的应用,搬移后就找不到文件。即使路径正确,OneDrive 停止或网络不稳时,仅联机的文件也可能打不开。先确认目标路径是否在 OneDrive 底下,并用 attrib 命令检查是否有 U(仅联机)。权宜办法是在右键选单选「始终保留在此设备上」,把实体留在本机。
- 程序能判断文件是否仅联机吗?
- 可以。仅联机的占位符带有 FILE_ATTRIBUTE_OFFLINE 与 FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS(0x00400000)等属性,因此不必下载内容就能从文件属性判断状态。取得属性或枚举文件夹不会引发水合(下载)。在 .NET 里有些值未定义于 FileAttributes,因此要转成整数再用位元运算检查。若确实需要开启但不读内容,也可用 CreateFile 的 FILE_FLAG_OPEN_NO_RECALL 这类手段。
- 关掉按需文件就能解决问题吗?
- 把关闭当成最后手段。关掉后同步范围内的每个文件都会下载到本机,磁盘容量与第一次同步的网络负载都会变大,Microsoft 也建议维持开启。实践上较有弹性的做法是只把业务应用会读的文件夹设成「始终保留在此设备上」(固定)。更根本、也更可靠的修正,是重新设计,让应用的文件夹与导入文件夹不要落在 OneDrive 管理之下。
- 我设了「始终保留在此设备上」,有些文件最后还是变回云朵图示。为什么?
- 先用 attrib 命令确认文件真的有固定(P 属性)。已固定的文件不在存储感知自动转成仅联机的范围内,但只因有人开启而「本机可用」、并未固定的文件,可能依存储感知设定与策略在一段时间后回到仅联机。使用者自己的「释放空间」操作,以及把小组网站文件改成仅联机的策略(DehydrateSyncedTeamSites),也会让云朵图示回来。业务上必须留在本机的文件夹,请以文件夹范围固定来运营。