CfGetPlaceholderRangeInfoForHydration 函数 (cfapi.h)

获取有关占位符文件或文件夹的范围信息。 此范围信息与 CfGetPlaceholderRangeInfo 返回的内容相同。 但是,它不采用 fileHandle 作为参数。 相反,它使用 ConnectionKeyTransferKeyFileId 来标识请求其范围信息的文件和流。

该平台向通过 CfConnectSyncRoot 注册的所有回调函数提供 ConnectionKey、TransferKeyFileId,提供程序可以使用这些参数从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

数据范围的起点的偏移量。 StartingOffsetRangeLength 在占位符文件中指定一个范围,该占位符文件中请求 了 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 堆栈的筛选器消息端口直接与筛选器通信。 因此,任何中间微型筛选器都不能妨碍 CreateFileFSCTL_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

另请参阅

CF_PLACEHOLDER_RANGE_INFO_CLASS

CfGetPlaceholderRangeInfo

CfConnectSyncRoot

CfGetPlatformInfo

CfGetTransferKey