CfGetPlaceholderRangeInfoForHydration 函数 (cfapi.h)

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

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

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

另请参阅

CF_PLACEHOLDER_RANGE_INFO_CLASS

CfGetPlaceholderRangeInfo

CfConnectSyncRoot

CfGetPlatformInfo

CfGetTransferKey