Windows10文件云/同步提供程序API-TransferData问题
我在建设云镜样品和具有类似问题这一个
下面是所涉及的测试代码:
// 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 大小的同步根文件夹中只创建了一个占位符文件。创建此后,我双击它或右键单击并“始终保留在此设备上” - 结果相同。
结果是在最后一次调用TransferDataWindows 资源管理器文件进度后卡住了。它最终会超时,我点击“再试一次”,我里面的断点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:
同步状态
注意 此成员是 Windows 10 版本 1803 的新成员。
平台的当前同步状态。
平台在云文件占位符上的任何失败操作时查询此信息。如果结构可用,平台将使用提供的信息为用户构建更有意义和可操作的消息。平台会将这些信息保留在文件中,直到它的最后一个句柄消失。如果为空,平台将清除之前设置的同步状态,如果有的话。
- 编辑 -
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 %sn", 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 %08xn", 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 的文件总是卡住并因“云操作无效”异常而失败。我能够通过这个修复来克服它,但我使用了来自 CF_CALLBACK_INFO.FileSize 的完整文件长度,而不是 CF_CALLBACK_PARAMETERS.FETCHDATA.RequiredLength 和 OptionalLength。看起来在最近的 Windows 更新之后 OptionalLength 始终为零。我的代码在 .NET 中,但我想您可以轻松将其转换为 C++:
// Use a complete file size in case the file is over 4Gb
if (callbackInfo.FileSize > 0x100000000)
{
requiredLength = callbackInfo.FileSize;
}