获取有关占位符文件或文件夹的范围信息。 此范围信息与 CfGetPlaceholderRangeInfo 返回的内容相同。 但是,它不采用 fileHandle 作为参数。 相反,它使用 ConnectionKey、 TransferKey 和 FileId 来标识请求其范围信息的文件和流。
该平台向通过 CfConnectSyncRoot 注册的所有回调函数提供 ConnectionKey、TransferKey 和 FileId,提供程序可以使用这些参数从CF_CALLBACK_TYPE_FETCH_DATA回调获取有关占位符的范围信息,而无需打开文件的句柄。
如果文件不是云文件占位符,API 将失败。 成功后,根据请求的特定 InfoClass 返回范围信息。
注释
仅当从 CfGetPlatformInfo0x600 获取的 API 或更高版本时PlatformVersion.IntegrationNumber,此 API 才可用。
Syntax
HRESULT CfGetPlaceholderRangeInfoForHydration(
[in] CF_CONNECTION_KEY ConnectionKey,
[in] CF_TRANSFER_KEY TransferKey,
[in] LARGE_INTEGER FileId,
[in] CF_PLACEHOLDER_RANGE_INFO_CLASS InfoClass,
[in] LARGE_INTEGER StartingOffset,
[in] LARGE_INTEGER RangeLength,
[out] PVOID InfoBuffer,
[in] DWORD InfoBufferSize,
[out, optional] PDWORD InfoBufferWritten
);
参数
[in] ConnectionKey
CfConnectSyncRoot 为同步提供程序管理的同步根创建的不透明句柄。 它还在CF_CALLBACK_TYPE_FETCH_DATA回调和其他回调的CF_CALLBACK_INFO中返回。
[in] TransferKey
已为其调用 CF_CALLBACK_TYPE_FETCH_DATA回调的 占位符文件的不透明句柄。 它还在CF_CALLBACK_TYPE_FETCH_DATA回调中的CF_CALLBACK_INFO中返回。 如果 API 未从CF_CALLBACK_TYPE_FETCH_DATA回调调用,则 CfGetTransferKey 也可以获取该 API。
[in] FileId
要服务的占位符文件/目录的 64 位文件系统维护的卷范围唯一 ID。 与 TransferKey 一样,这会在CF_CALLBACK_TYPE_FETCH_DATA和其他回调的CF_CALLBACK_INFO中返回,以便提供程序不必再次检索它。
[in] InfoClass
占位符数据范围的类型。 值可以是下列任一值:
| 价值 | Description |
|---|---|
| CF_PLACEHOLDER_RANGE_INFO_ONDISK | 磁盘数据是文件中物理存在的数据。 这是一组其他类型的范围。 |
| CF_PLACEHOLDER_RANGE_INFO_VALIDATED | 已验证的数据是当前与云同步的磁盘数据子集。 |
| CF_PLACEHOLDER_RANGEINFO_MODIFIED | 修改后的数据是磁盘上数据子集,当前未与云同步(即已修改或追加)。 |
[in] StartingOffset
数据范围的起点的偏移量。 StartingOffset 和 RangeLength 在占位符文件中指定一个范围,该占位符文件中请求 了 InfoClass 参数所描述的信息
[in] RangeLength
数据范围的长度。 提供程序可以指定 CF_EOFRangeLength ,以指示请求其信息的范围从 StartingOffset 到文件末尾。
[out] InfoBuffer
指向将接收数据的缓冲区的指针。 缓冲区是 一个由CF_FILE_RANGE 结构构成的数组,这些结构是偏移/长度对,用于描述所请求的范围。
[in] InfoBufferSize
InfoBuffer 的长度(以字节为单位)。
[out, optional] InfoBufferWritten
接收 InfoBuffer 中返回的字节数。
返回值
如果此函数成功,则返回 S_OK。 否则,它将返回 HRESULT 错误代码。 下表列出了一些常见的错误代码:
| 错误代码 | Meaning |
|---|---|
| HRESULT_FROM_WIN32(ERROR_HANDLE_EOF) | 这意味着 StartingOffset>= 文件末尾的位置。 |
| HRESULT_FROM_WIN32(ERROR_MORE_DATA) | 这意味着下一 个CF_FILE_RANGE 条目不适合提供的缓冲区。 调用方应验证是否收到任何条目,或者是否使用返回 的 InfoBufferWritten 值。 |
注解
虽然已存在用于查询占位符的冻结文件范围的 API,但需要新的 API 来提高平台的可靠性。
现有 API CfGetPlaceholderRangeInfo 需要文件的打开句柄,然后使用该句柄触发 FSCTL_HSM_CONTROL 。 提供程序/同步引擎通常使用此 API 来评估文件哪些部分未从筛选器调用 的CF_CALLBACK_TYPE_FETCH_DATA 回调上下文中冻结,以冻结文件以满足 IO。
当提供程序/同步引擎尝试打开要作为参数传递给 CfGetPlaceholderRangeInfo 的文件的句柄时,IO 堆栈中的微型筛选器可能会对文件发出数据扫描。 或者,微型筛选器可能会阻止 CfGetPlaceholderRangeInfo 在内部触发的FSCTL_HSM_CONTROL。
cldflt 筛选器旨在根据所需的文件范围只调用一个CF_CALLBACK_TYPE_FETCH_DATA回调来解除文件冻结。 由于上述任一情况,数据扫描会卡在原始 CF_CALLBACK_TYPE_FETCH_DATA 后面,或者 CF_CALLBACK_TYPE_FETCH_DATA 卡在阻止的 FSCTL 后面。 这会导致冻结路径中的死锁。
因此,需要此 API。 它执行与 CfGetPlaceholderRangeInfo 相同的功能,但使用绕过中间 IO 堆栈的筛选器消息端口直接与筛选器通信。 因此,任何中间微型筛选器都不能妨碍 CreateFile 或 FSCTL_HSM_CONTROL。
请注意,调用方始终通过 CfConnectSyncRoot 获取 ConnectionKey。 它可以通过 CfGetTransferKey 获取 TransferKey,并使用 GetFileInformationByHandle 获取 FileId。 但此方法需要打开文件的句柄,因此与使用 CfGetPlaceholderRangeInfo 不同。
总之,当需要 CF_CALLBACK_TYPE_FETCH_DATA 回调上下文中的范围信息时,应使用此 API。 在所有其他情况下,包括当提供程序想要冻结文件而不被筛选器请求时,应使用 CfGetPlaceholderRangeInfo 。 平台无法识别特定上下文中调用的 API,因此 onus 位于提供程序/同步引擎上以执行正确的作。
例子
这是一个简单的示例,该函数传递的 InfoBuffer 足以一次只检索一个 CF_FILE_RANGE 条目。 实际上,调用方可以传递一个 InfoBuffer ,该信息可能对应于每个 API 调用的多个 CF_FILE_RANGE 条目。 如果需要,可以使用错误代码 HRESULT_FROM_WIN32(ERROR_MORE_DATA) 传递更大的缓冲区。
#include <cfapi.h>
// ******************************************************************************************************
// From within the CF_CALLBACK_TYPE_FETCH_DATA Callback, the provider can use
// g_PlatformInfo.IntegrationNumber to see if the new API is supported. If it is, the provider can pass
// ConnectionKey, TransferKey and FileId along with other parameters to obtain information about file
// ranges which have already been hydrated.
// *******************************************************************************************************
// The provider could obtain file ranges that are hydrated like this:
std::vector<CF_FILE_RANGE> hydratedRanges = GetFileRangesFromCallback( CallbackInfo->ConnectionKey,
CallbackInfo->TransferKey,
CallbackInfo->FileId,
CF_PLACEHOLDER_RANGE_INFO_ONDISK
0,
CF_EOF);
// Based on these hydratedRanges, the provider can chose to hydrate only ranges which aren’t on the disk.
// ******************************************************************************************************
// Implementation of a function that eventually calls this API.
// ******************************************************************************************************
typedef HRESULT( __stdcall* t_CfGetPlaceholderRangeInfoForHydration )(
CF_CONNECTION_KEY ConnectionKey,
CF_TRANSFER_KEY TransferKey,
LARGE_INTEGER FileId,
CF_PLACEHOLDER_RANGE_INFO_CLASS InfoClass,
LARGE_INTEGER StartingOffset,
LARGE_INTEGER RangeLength,
PVOID InfoBuffer,
DWORD InfoBufferSize,
PDWORD InfoBufferWritten );
t_CfGetPlaceholderRangeInfoForHydration _CfGetPlaceholderRangeInfoForHydration = nullptr;
std::vector<CF_FILE_RANGE>
GetFileRangesFromCallback( CF_CONNECTION_KEY ConnectionKey,
CF_TRANSFER_KEY TransferKey,
LARGE_INTEGER FileId,
CF_PLACEHOLDER_RANGE_INFO_CLASS RangeInfoClass,
long long StartOffset,
long long Length,
PBOOLEAN UseOldAPI )
{
long long StartOffset = 0;
CF_FILE_RANGE fileRange;
long long Length = 0;
LARGE_INTEGER queryOffset = ll2li( StartOffset );
LARGE_INTEGER queryLength = ll2li( Length );
DWORD inforBufferWritten = 0;
// This will contain all the hydrated ranges in the file if the function succeeds.
std::vector<CF_FILE_RANGE> ranges;
bool stop = false;
CF_PLATFORM_INFO platformInfo;
hr = (CfGetPlatformInfo( &platformInfo ));
if(FAILED(hr)) {
*UseOldAPI = TRUE;
return ranges; //empty.
}
if (platformInfo.IntegrationNumber < 600) {
*UseOldAPI = TRUE;
return ranges; //empty.
}
wil::unique_hmodule CloudFilesApi( LoadLibrary( L"cldapi.dll" ) );
THROW_LAST_ERROR_IF_NULL( CloudFilesApi );
_CfGetPlaceholderRangeInfoForHydration = reinterpret_cast<t_CfGetPlaceholderRangeInfoForHydration>(
GetProcAddress( CloudFilesApi.get(), "CfGetPlaceholderRangeInfoForHydration" ) );
THROW_LAST_ERROR_IF_NULL( _CfGetPlaceholderRangeInfoForHydration );
while ( !stop ) {
hr = _CfGetPlaceholderRangeInfoForHydration ( ConnectionKey,
TransferKey,
FileId,
RangeInfoClass,
queryOffset,
queryLength,
&fileRange,
sizeof( fileRange ),
&infoBufferWritten );
if ( hr == HRESULT_FROM_WIN32( ERROR_HANDLE_EOF ) ||
hr == HRESULT_FROM_WIN32( ERROR_MORE_DATA ) ) {
// We need to break the loop only if there is no more data.
if ( hr == HRESULT_FROM_WIN32( ERROR_HANDLE_EOF ) ) {
stop = true;
}
hr = S_OK;
}
if ( FAILED( hr ) || infoBufferWritten == 0 ) {
return ranges;
}
ranges.push_back( fileRange );
queryOffset.QuadPart = fileRange.StartingOffset.QuadPart + fileRange.Length.QuadPart;
if ( Length != CF_EOF && queryOffset.QuadPart >= ( StartOffset + Length ) ) {
stop = true;
} else if ( Length != CF_EOF) {
// Update the new query length
queryLength.QuadPart = StartOffset + Length - queryOffset.QuadPart
if ( queryLength.QuadPart <= 0 ) {
stop = true;
}
}
}
return ranges;
}
要求
| Requirement | 价值 |
|---|---|
| Header | cfapi.h |
| Library | cldapi.lib |