CfGetPlaceholderRangeInfoForHydration 函数 (cfapi.h)
获取有关占位符文件或文件夹的范围信息。 此范围信息与 CfGetPlaceholderRangeInfo 返回的信息相同。 但是,它不会将 fileHandle 作为参数。 相反,它使用 ConnectionKey、 TransferKey 和 FileId 来标识正在为其请求范围信息的文件和流。
平台为通过 CfConnectSyncRoot 注册的所有回调函数提供 ConnectionKey、TransferKey 和 FileId,提供程序可以使用这些参数从CF_CALLBACK_TYPE_FETCH_DATA回调获取占位符的范围信息,而无需它打开文件的句柄。
如果文件不是云文件占位符,API 将失败。 成功后,将根据请求的特定 InfoClass 返回范围信息。
注意
仅当从 CfGetPlatformInfo 获取的 为0x600
或更高时,PlatformVersion.IntegrationNumber
此 API 才可用。
语法
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中返回。 如果不从CF_CALLBACK_TYPE_FETCH_DATA回调调用 API,则 CfGetTransferKey 也可以获取此 API。
[in] FileId
要服务的占位符文件/目录的 64 位文件系统维护的卷范围唯一 ID。 与 TransferKey 一样,它在 CF_CALLBACK_TYPE_FETCH_DATA 和其他回调的 CF_CALLBACK_INFO 中返回,因此提供程序不必再次检索它。
[in] InfoClass
占位符数据范围的类型。 值可以是下列任一值:
“值” | 说明 |
---|---|
CF_PLACEHOLDER_RANGE_INFO_ONDISK | 磁盘上数据是文件中实际存在的数据。 这是其他类型的范围的超级集。 |
CF_PLACEHOLDER_RANGE_INFO_VALIDATED | 验证数据是当前与云同步的磁盘上数据的子集。 |
CF_PLACEHOLDER_RANGEINFO_MODIFIED | 修改的数据是磁盘上数据的子集,当前未与云 (同步,即修改或追加。) |
[in] StartingOffset
数据范围的起始点偏移量。 StartingOffset 和 RangeLength 在占位符文件中指定一个区域,请求 InfoClass 参数所描述的信息
[in] RangeLength
数据范围的长度。 提供程序可以为 RangeLength 指定CF_EOF
,以指示请求其信息的范围从 StartingOffset 到文件末尾。
[out] InfoBuffer
指向将接收数据的缓冲区的指针。 缓冲区是 CF_FILE_RANGE 结构的数组,这些结构是偏移/长度对,用于描述请求的范围。
[in] InfoBufferSize
InfoBuffer 的长度(以字节为单位)。
[out, optional] InfoBufferWritten
接收 InfoBuffer 中返回的字节数。
返回值
如果此函数成功,则返回 S_OK
。 否则,将返回 HRESULT 错误代码。 下表中列出了一些常见的错误代码:
错误代码 | 含义 |
---|---|
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,因此,提供程序/同步引擎需要执行正确的操作。
示例
这是一个简单的示例,其中函数传递的 InfoBuffer 足以一次只检索一 个CF_FILE_RANGE 条目。 实际上,调用方可以传递一个 InfoBuffer,该 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;
}
要求
要求 | 值 |
---|---|
Header | cfapi.h |