in ProjectedFSLib.Managed.API/VirtualizationInstance.cpp [450:704]
HResult VirtualizationInstance::StartVirtualizing(IRequiredCallbacks^ requiredCallbacks)
{
if (m_virtualizationContextGc != nullptr)
{
return HResult::AlreadyInitialized;
}
// Store the provider's implementation of the required callbacks.
m_requiredCallbacks = requiredCallbacks;
// Create a handle to this VirtualizationInstance object so that it can be passed to the native
// lib as the InstanceContext parameter to PrjStartVirtualizing().
m_virtualizationContextGc = new gcroot<VirtualizationInstance^>();
*(m_virtualizationContextGc) = this;
HRESULT startHr = S_OK;
if (m_apiHelper->UseBetaApi)
{
// Query the file system for sector alignment info that CreateWriteBuffer() will need.
FindBytesPerSectorAndAlignment();
PRJ_COMMAND_CALLBACKS callbacks;
m_apiHelper->_PrjCommandCallbacksInit(sizeof(callbacks), &callbacks);
// Set the native wrappers for the required callbacks.
callbacks.PrjStartDirectoryEnumeration = reinterpret_cast<PRJ_START_DIRECTORY_ENUMERATION_CB*>(PrjStartDirectoryEnumerationCB);
callbacks.PrjEndDirectoryEnumeration = reinterpret_cast<PRJ_END_DIRECTORY_ENUMERATION_CB*>(PrjEndDirectoryEnumerationCB);
callbacks.PrjGetDirectoryEnumeration = reinterpret_cast<PRJ_GET_DIRECTORY_ENUMERATION_CB*>(PrjGetDirectoryEnumerationCB);
callbacks.PrjGetPlaceholderInformation = reinterpret_cast<PRJ_GET_PLACEHOLDER_INFORMATION_CB*>(PrjGetPlaceholderInformationCB);
callbacks.PrjGetFileStream = reinterpret_cast<PRJ_GET_FILE_STREAM_CB*>(PrjGetFileStreamCB);
// Set native wrappers for the optional callbacks if the provider has an implementation for them.
if (OnQueryFileName != nullptr)
{
callbacks.PrjQueryFileName = reinterpret_cast<PRJ_QUERY_FILE_NAME_CB*>(PrjQueryFileNameCB);
}
if (OnCancelCommand != nullptr)
{
callbacks.PrjCancelCommand = reinterpret_cast<PRJ_CANCEL_COMMAND_CB*>(PrjCancelCommandCB);
}
// We set the native wrapper for the notify callback if the provider set at least one OnNotify* property.
if ((OnNotifyFileOpened != nullptr) ||
(OnNotifyNewFileCreated != nullptr) ||
(OnNotifyFileOverwritten != nullptr) ||
(OnNotifyPreDelete != nullptr) ||
(OnNotifyPreRename != nullptr) ||
(OnNotifyPreCreateHardlink != nullptr) ||
(OnNotifyFileRenamed != nullptr) ||
(OnNotifyHardlinkCreated != nullptr) ||
(OnNotifyFileHandleClosedNoModification != nullptr) ||
(OnNotifyFileHandleClosedFileModifiedOrDeleted != nullptr) ||
(OnNotifyFilePreConvertToFull != nullptr))
{
callbacks.PrjNotifyOperation = reinterpret_cast<PRJ_NOTIFY_OPERATION_CB*>(PrjNotifyOperationCB);
}
pin_ptr<const WCHAR> rootPath = PtrToStringChars(m_virtualizationRootPath);
// Use a temp location to avoid e0158.
pin_ptr<PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT> tempHandle = &(m_virtualizationContext);
auto instanceHandle = reinterpret_cast<PRJ_VIRTUALIZATIONINSTANCE_HANDLE*>(tempHandle);
VIRTUALIZATION_INST_EXTENDED_PARAMETERS extendedParameters;
memset(&extendedParameters, 0, sizeof(VIRTUALIZATION_INST_EXTENDED_PARAMETERS));
extendedParameters.Size = sizeof(VIRTUALIZATION_INST_EXTENDED_PARAMETERS);
extendedParameters.Flags = m_enableNegativePathCache ? PRJ_FLAG_INSTANCE_NEGATIVE_PATH_CACHE : 0;
extendedParameters.PoolThreadCount = m_poolThreadCount;
extendedParameters.ConcurrentThreadCount = m_concurrentThreadCount;
if (m_notificationMappings->Count > 0)
{
std::unique_ptr<PRJ_NOTIFICATION_MAPPING[]> nativeNotificationMappings;
// "Pinning pointers can only be declared as non-static local variables on the stack."
// https://docs.microsoft.com/en-us/cpp/windows/pin-ptr-cpp-cli
// And so lets keep a copy of the paths alive rather than trying to pin the incoming strings
std::vector<std::wstring> notificationPaths;
notificationPaths.reserve(m_notificationMappings->Count);
nativeNotificationMappings.reset(new PRJ_NOTIFICATION_MAPPING[m_notificationMappings->Count]);
int index = 0;
for each(NotificationMapping^ mapping in m_notificationMappings)
{
nativeNotificationMappings[index].NotificationBitMask = static_cast<PRJ_NOTIFY_TYPES>(mapping->NotificationMask);
pin_ptr<const WCHAR> path = PtrToStringChars(mapping->NotificationRoot);
notificationPaths.push_back(std::wstring(path));
nativeNotificationMappings[index].NotificationRoot = notificationPaths.rbegin()->c_str();
++index;
}
extendedParameters.NotificationMappings = nativeNotificationMappings.get();
extendedParameters.NumNotificationMappingsCount = m_notificationMappings->Count;
startHr = m_apiHelper->_PrjStartVirtualizationInstanceEx(rootPath,
&callbacks,
m_virtualizationContextGc,
&extendedParameters,
instanceHandle);
}
else
{
// The caller didn't provide any notification mappings. Use the non-Ex Start routine to get
// ProjFS to supply the default notification mask.
startHr = m_apiHelper->_PrjStartVirtualizationInstance(rootPath,
&callbacks,
extendedParameters.Flags,
0, // ProjFS will default to PRJ_DEFAULT_NOTIFICATION_MASK defined in prjlibp.h
extendedParameters.PoolThreadCount,
extendedParameters.ConcurrentThreadCount,
m_virtualizationContextGc,
instanceHandle);
}
}
else
{
PRJ_CALLBACKS callbacks;
memset(&callbacks, 0, sizeof(PRJ_CALLBACKS));
// Set native wrappers for the required callbacks.
callbacks.StartDirectoryEnumerationCallback = reinterpret_cast<PRJ_START_DIRECTORY_ENUMERATION_CB*>(PrjStartDirectoryEnumerationCB);
callbacks.EndDirectoryEnumerationCallback = reinterpret_cast<PRJ_END_DIRECTORY_ENUMERATION_CB*>(PrjEndDirectoryEnumerationCB);
callbacks.GetDirectoryEnumerationCallback = reinterpret_cast<PRJ_GET_DIRECTORY_ENUMERATION_CB*>(PrjGetDirectoryEnumerationCB);
callbacks.GetPlaceholderInfoCallback = reinterpret_cast<PRJ_GET_PLACEHOLDER_INFO_CB*>(PrjGetPlaceholderInfoCB);
callbacks.GetFileDataCallback = reinterpret_cast<PRJ_GET_FILE_DATA_CB*>(PrjGetFileDataCB);
// Set native wrappers for the optional callbacks if the provider has an implementation for them.
if (OnQueryFileName != nullptr)
{
callbacks.QueryFileNameCallback = reinterpret_cast<PRJ_QUERY_FILE_NAME_CB*>(PrjQueryFileNameCB);
}
if (OnCancelCommand != nullptr)
{
callbacks.CancelCommandCallback = reinterpret_cast<PRJ_CANCEL_COMMAND_CB*>(PrjCancelCommandCB);
}
// We set the native wrapper for the notification callback if the provider set at least one OnNotify* property.
if ((OnNotifyFileOpened != nullptr) ||
(OnNotifyNewFileCreated != nullptr) ||
(OnNotifyFileOverwritten != nullptr) ||
(OnNotifyPreDelete != nullptr) ||
(OnNotifyPreRename != nullptr) ||
(OnNotifyPreCreateHardlink != nullptr) ||
(OnNotifyFileRenamed != nullptr) ||
(OnNotifyHardlinkCreated != nullptr) ||
(OnNotifyFileHandleClosedNoModification != nullptr) ||
(OnNotifyFileHandleClosedFileModifiedOrDeleted != nullptr) ||
(OnNotifyFilePreConvertToFull != nullptr))
{
callbacks.NotificationCallback = reinterpret_cast<PRJ_NOTIFICATION_CB*>(PrjNotificationCB);
}
pin_ptr<const WCHAR> rootPath = PtrToStringChars(m_virtualizationRootPath);
pin_ptr<PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT> namespaceCtx = &(m_virtualizationContext);
PRJ_STARTVIRTUALIZING_OPTIONS startOptions;
memset(&startOptions, 0, sizeof(PRJ_STARTVIRTUALIZING_OPTIONS));
startOptions.Flags = m_enableNegativePathCache ? PRJ_FLAG_USE_NEGATIVE_PATH_CACHE : PRJ_FLAG_NONE;
startOptions.PoolThreadCount = m_poolThreadCount;
startOptions.ConcurrentThreadCount = m_concurrentThreadCount;
// These need to stay in scope until we call PrjStartVirtualizing
std::unique_ptr<PRJ_NOTIFICATION_MAPPING[]> nativeNotificationMappings;
std::vector<std::wstring> notificationPaths;
if (m_notificationMappings->Count > 0)
{
// "Pinning pointers can only be declared as non-static local variables on the stack."
// https://docs.microsoft.com/en-us/cpp/windows/pin-ptr-cpp-cli
// And so lets keep a copy of the paths alive rather than trying to pin the incoming strings
notificationPaths.reserve(m_notificationMappings->Count);
nativeNotificationMappings.reset(new PRJ_NOTIFICATION_MAPPING[m_notificationMappings->Count]);
int index = 0;
for each(NotificationMapping^ mapping in m_notificationMappings)
{
nativeNotificationMappings[index].NotificationBitMask = static_cast<PRJ_NOTIFY_TYPES>(mapping->NotificationMask);
pin_ptr<const WCHAR> path = PtrToStringChars(mapping->NotificationRoot);
notificationPaths.push_back(std::wstring(path));
nativeNotificationMappings[index].NotificationRoot = notificationPaths.rbegin()->c_str();
++index;
}
startOptions.NotificationMappings = nativeNotificationMappings.get();
startOptions.NotificationMappingsCount = m_notificationMappings->Count;
}
else
{
// ProjFS will supply a default notification mask for the root if there are no mappings.
startOptions.NotificationMappingsCount = 0;
}
startHr = m_apiHelper->_PrjStartVirtualizing(rootPath,
&callbacks,
m_virtualizationContextGc,
&startOptions,
namespaceCtx);
}
if (FAILED(startHr))
{
return static_cast<HResult>(startHr);
}
// Store the virtualization instance ID.
GUID instanceId = {};
Guid^ instanceIDRef;
HRESULT getInfoHr = S_OK;
if (m_apiHelper->UseBetaApi)
{
getInfoHr = m_apiHelper->_PrjGetVirtualizationInstanceIdFromHandle(reinterpret_cast<PRJ_VIRTUALIZATIONINSTANCE_HANDLE>(m_virtualizationContext),
&instanceId);
}
else
{
PRJ_VIRTUALIZATION_INSTANCE_INFO instanceInfo = {};
getInfoHr = m_apiHelper->_PrjGetVirtualizationInstanceInfo(m_virtualizationContext,
&instanceInfo);
instanceId = instanceInfo.InstanceID;
}
// If we couldn't get the instance ID, shut the instance down and return error (this is super
// unlikely; it could only happen if somehow the instance successfully started above, but its
// info is corrupt).
if (FAILED(getInfoHr))
{
StopVirtualizing();
return static_cast<HResult>(getInfoHr);
}
instanceIDRef = gcnew Guid(instanceId.Data1,
instanceId.Data2,
instanceId.Data3,
instanceId.Data4[0],
instanceId.Data4[1],
instanceId.Data4[2],
instanceId.Data4[3],
instanceId.Data4[4],
instanceId.Data4[5],
instanceId.Data4[6],
instanceId.Data4[7]);
m_virtualizationInstanceID = *instanceIDRef;
return HResult::Ok;
}