Windows 10 文件云/同步提供程序 API - 传输数据问题

Windows 10 File Cloud / Sync Provider API - TransferData problem

我正在 Cloud Mirror Sample and having a similar issue to

这里是涉及到的测试代码:

        // When the client needs to fetch data from the cloud, this method will be called.
    // The FakeMirrorDataMover class does the actual work of copying files from
    // the "cloud" to the "client" and updating the transfer status along the way.
    void CALLBACK FakeCloudProvider::OnFetchData(
        _In_ CONST CF_CALLBACK_INFO* callbackInfo,
        _In_ CONST CF_CALLBACK_PARAMETERS* callbackParameters)
    {
        //FileCopierWithProgress::CopyFromServerToClient(callbackInfo, callbackParameters, ProviderFolderLocations::GetServerFolder());
    
            const UINT CHUNKSIZE = 48 * 1024 * 1024;
            UINT len;
            LONG64 offset = callbackParameters->FetchData.RequiredFileOffset.QuadPart;
            LONG64 requiredLength = callbackParameters->FetchData.RequiredLength.QuadPart;
            byte *buffer = new byte[CHUNKSIZE];
            FillMemory(buffer, CHUNKSIZE, (byte)0xA5);
    
            while (0 < requiredLength)
            {
                len = requiredLength < CHUNKSIZE ? requiredLength : CHUNKSIZE;
                if (0 != len % 4096)
                    len = 4096 * (len / 4096 + 1);
    
                Placeholders::TransferData(callbackInfo->TransferKey.QuadPart, buffer, offset, len, 0);
    
                requiredLength -= len;
                offset += len;
            }
    
            delete[] buffer;
    }

HRESULT Placeholders::TransferData(
    //_In_ CF_CONNECTION_KEY connectionKey,
    _In_ LONG64 transferKey,
    _In_reads_bytes_opt_(length.QuadPart) LPCVOID transferData,
    _In_ LONG64 startingOffset,
    _In_ LONG64 length,
    _In_ NTSTATUS completionStatus)
{
    CF_OPERATION_INFO opInfo = { 0 };
    CF_OPERATION_PARAMETERS opParams = { 0 };

    opInfo.StructSize = sizeof(opInfo);
    opInfo.Type = CF_OPERATION_TYPE_TRANSFER_DATA;
    opInfo.ConnectionKey = FakeCloudProvider::GetConnectionKey();
    opInfo.TransferKey.QuadPart = transferKey;
    opParams.ParamSize = CF_SIZE_OF_OP_PARAM(TransferData);
    opParams.TransferData.CompletionStatus = completionStatus;
    opParams.TransferData.Buffer = transferData;
    opParams.TransferData.Offset.QuadPart = startingOffset;
    opParams.TransferData.Length.QuadPart = length;

    winrt::check_hresult(CfExecute(&opInfo, &opParams));     
    return S_OK;
}

同步根文件夹中仅创建了一个约 9.3 GB 大小的占位符文件。创建此文件后,我双击它或右键单击并“始终保持在此设备上”- 结果相同。

结果是,在最后一次调用 TransferData 之后,Windows Explorer 文件进度卡住了。它最终会超时,我点击“再试一次”,我在 OnFetchData() 内的断点被击中,文件进度仍然卡住。如果我取消传输,那么 onCancelFetchData() 会被调用一次。随后尝试下载文件的其余部分 将不再调用 onFetchData()

如果我注释掉 if (0 != len % 4096) len = 4096 * (len / 4096 + 1); 部分,那么我会得到可怕的 0x8007017c the cloud operation is invalid 以及上述其他症状。

我已经尝试使用较小的 ~ 9.3 MB 文件并且运行良好...还有什么可以尝试的?

编辑

~100MB 和~1GB 大小也可以正常工作

CfExecute 次调用的结果:

//[...] same output for all previous offsets except for the last call
TransferData method - offset: 1298137088, length: 50331648, syncStatBeforeCall: null, syncStatAfterCall: null, hresult: 0
TransferData method - offset: 1348468736, length: 50331648, syncStatBeforeCall: null, syncStatAfterCall: null, hresult: 0
TransferData method - offset: 1398800384, length: 11265024, syncStatBeforeCall: null, syncStatAfterCall: null, hresult: 8007017c

据我所知,null 适合 SyncStatus:

SyncStatus

Note This member is new for Windows 10, version 1803.

The current sync status of the platform.

The platform queries this information upon any failed operations on a cloud file placeholder. If a structure is available, the platform will use the information provided to construct a more meaningful and actionable message to the user. The platform will keep this information on the file until the last handle on it goes away. If null, the platform will clear the previously set sync status, if there is one.

--编辑--

HRESULT Placeholders::Create(_In_ PCWSTR destPath,
                          _In_ PCWSTR fileName,
                          _In_ CF_PLACEHOLDER_CREATE_FLAGS flags,
                          _In_ LONG64 fileSize,
                          _In_ DWORD fileAttributes,
                          _In_ LONG64  ftCreationTime,
                          _In_ LONG64  ftLastWriteTime,
                          _In_ LONG64  ftLastAccessTime,
                          _In_ LONG64  ftChangeTime)
{
    CF_PLACEHOLDER_CREATE_INFO cloudEntry;
        
    cloudEntry.FileIdentity = fileName;
    cloudEntry.FileIdentityLength = (DWORD)((wcslen(fileName)+1) * sizeof(WCHAR));
    cloudEntry.RelativeFileName = fileName;
    cloudEntry.Flags = flags;
    cloudEntry.FsMetadata.FileSize.QuadPart = fileSize;
    cloudEntry.FsMetadata.BasicInfo.FileAttributes = fileAttributes;
    cloudEntry.FsMetadata.BasicInfo.CreationTime.QuadPart = ftCreationTime;
    cloudEntry.FsMetadata.BasicInfo.LastWriteTime.QuadPart = ftLastWriteTime;
    cloudEntry.FsMetadata.BasicInfo.LastAccessTime.QuadPart = ftLastAccessTime;
    cloudEntry.FsMetadata.BasicInfo.ChangeTime.QuadPart = ftChangeTime;

    if ((fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
    {
        cloudEntry.Flags |= CF_PLACEHOLDER_CREATE_FLAG_DISABLE_ON_DEMAND_POPULATION;
        cloudEntry.FsMetadata.FileSize.QuadPart = 0;
        cloudEntry.FileIdentity = nullptr;
    }

    try
    {
        wprintf(L"Creating placeholder for %s\n", fileName);
        winrt::check_hresult(CfCreatePlaceholders(destPath, &cloudEntry, 1, CF_CREATE_FLAG_NONE, NULL));
    }
    catch (...)
    {
        // winrt::to_hresult() will eat the exception if it is a result of winrt::check_hresult,
        // otherwise the exception will get rethrown and this method will crash out as it should
        wprintf(L"Failed to create placeholder for %s with %08x\n", fileName, static_cast<HRESULT>(winrt::to_hresult()));
        // Eating it here lets other files still get a chance. Not worth crashing the sample, but
        // certainly noteworthy for production code
        return static_cast<HRESULT>(winrt::to_hresult());
    }

    return S_OK;
}

Create(_T("C:\SyncRootDT"), _T("file1"), CF_PLACEHOLDER_CREATE_FLAG_MARK_IN_SYNC,
            10000000000, FILE_ATTRIBUTE_NORMAL, 532657415, 532657415, 532657415, 532657415));

我观察到类似的行为。最大 4Gb 的文件水合工作正常,但超过 4Gb 的文件总是卡住并因 'Cloud operation is invalid' 异常而失败。我能够使用 this fix 来克服它,但我没有使用 CF_CALLBACK_PARAMETERS.FETCHDATA.RequiredLength 和 OptionalLength,而是使用了 CF_CALLBACK_INFO.FileSize 中的完整文件长度。看起来在最近的 Windows 更新之后,OptionalLength 始终为零。 我的代码在 .NET 中,但我想您可以轻松地将其转换为 C++:

// Use a complete file size in case the file is over 4Gb
if (callbackInfo.FileSize > 0x100000000)
{
    requiredLength = callbackInfo.FileSize;
}