Freigeben über


Benachrichtigungen zu Dateisystemvorgängen

Neben den erforderlichen Rückrufen für die Verarbeitung von Enumerationen, dem Zurückgeben von Dateimetadaten und Dateiinhalten kann ein Anbieter optional einen PRJ_NOTIFICATION_CB-Rückruf im Aufruf von PrjStartVirtualizing registrieren. Dieser Rückruf empfängt Benachrichtigungen zu Dateisystemvorgänge, die für Dateien und Verzeichnisse unter dem Virtualisierungsstamm der Instanz ausgeführt werden. Einige der Benachrichtigungen sind „Nach-Vorgang”-Benachrichtigungen, die dem Anbieter mitteilen, dass ein Vorgang gerade abgeschlossen wurde. Die anderen Benachrichtigungen sind „Vorabbenachrichtigungen”, d. h. der Anbieter wird vor dem Vorgang benachrichtigt. In einer Vorabbenachrichtigung kann der Anbieter den Vorgang ablehnen, indem er einen Fehlercode aus dem Rückruf zurückgibt. Dies führt dazu, dass der Vorgang mit dem zurückgegebenen Fehlercode fehlschlägt.

Angeben der Art der zu empfangenden Benachrichtigungen

Wenn der Anbieter beim Starten einer Virtualisierungsinstanz eine Implementierung von PRJ_NOTIFICATION_CB bereitstellt, sollte auch angegeben werden, welche Benachrichtigungen empfangen werden sollen. Die Benachrichtigungen, für die sich der Anbieter registrieren kann, sind in der PRJ_NOTIFICATION-Enumeration definiert.

Wenn der Anbieter die Benachrichtigungen angibt, die er erhalten möchte, erfolgt dies mithilfe eines Arrays einer oder mehrerer PRJ_NOTIFICATION_MAPPING-Strukturen. Diese definieren „Benachrichtigungszuordnungen“. Eine Benachrichtigungszuordnung ist eine Verknüpfung zwischen einem Verzeichnis, das als „Benachrichtigungsstamm“ bezeichnet wird, und einer Reihe von Benachrichtigungen, die als Bitmaske ausgedrückt werden und die ProjFS für dieses Verzeichnis und seine Nachkommen senden sollte. Eine Benachrichtigungszuordnung kann auch für eine einzelne Datei eingerichtet werden. Eine Datei oder ein Verzeichnis, die in einer Benachrichtigungszuordnung angegeben sind, müssen zum Zeitpunkt, zu dem der Anbieter PrjStartVirtualizing aufruft, nicht vorhanden sein. Der Anbieter kann einen beliebigen Pfad angeben, und die Zuordnung wird für sie gelten, falls sie jemals erstellt wird.

Wenn der Anbieter mehrere Benachrichtigungszuordnungen angibt und einige davon von anderen abgeleitet sind, müssen die Zuordnungen in absteigender Reihenfolge angegeben werden. Benachrichtigungszuordnungen auf tieferen Ebenen überschreiben die Zuordnungen auf höheren Ebenen für deren Nachkommen.

Wenn der Anbieter keine Gruppe von Benachrichtigungszuordnungen angibt, sendet ProjFS standardmäßig PRJ_NOTIFY_FILE_OPENED, PRJ_NOTIFY_NEW_FILE_CREATED und PRJ_NOTIFY_FILE_OVERWRITTEN für alle Dateien und Verzeichnisse unter dem Virtualisierungsstamm.

Der folgende Codeausschnitt veranschaulicht, wie beim Starten einer Virtualisierungsinstanz eine Reihe von Benachrichtigungen registriert wird.

PRJ_CALLBACKS callbackTable;

// Supply required callbacks.
callbackTable.StartDirectoryEnumerationCallback = MyStartEnumCallback;
callbackTable.EndDirectoryEnumerationCallback = MyEndEnumCallback;
callbackTable.GetDirectoryEnumerationCallback = MyGetEnumCallback;
callbackTable.GetPlaceholderInfoCallback = MyGetPlaceholderInfoCallback;
callbackTable.GetFileDataCallback = MyGetFileDataCallback;

// The rest of the callbacks are optional.  We want notifications, so specify
// our notification callback.
callbackTable.QueryFileNameCallback = nullptr;
callbackTable.NotificationCallback = MyNotificationCallback;
callbackTable.CancelCommandCallback = nullptr;

// Assume the provider has created a new virtualization root at C:\VirtRoot and
// initialized it with this layout:
//
//     C:\VirtRoot
//     +--- baz
//     \--- foo
//          +--- subdir1
//          \--- subdir2
//
// The provider wants these notifications:
// * Notification of new file/directory creates for all locations within the
//   virtualization root that are not subject to one of the following more
//   specific notification mappings.
// * Notification of new file/directory creates, file opens, post-renames, and
//   pre- and post-deletes for C:\VirtRoot\foo
// * No notifications for C:\VirtRoot\foo\subdir1
PRJ_STARTVIRTUALIZING_OPTIONS startOpts = {};
PRJ_NOTIFICATION_MAPPING notificationMappings[3];

// Configure default notifications - notify of new files for most of the tree.
notificationMappings[0].NotificationRoot = L"";
notificationMappings[0].NotificationBitMask = PRJ_NOTIFY_NEW_FILE_CREATED;

// Override default notifications - notify of new files, opened files, and file
// deletes for C:\VirtRoot\foo and its descendants.
notificationMappings[1].NotificationRoot = L"foo";
notificationMappings[1].NotificationBitMask = PRJ_NOTIFY_NEW_FILE_CREATED |
                                              PRJ_NOTIFY_FILE_OPENED |
                                              PRJ_NOTIFY_PRE_DELETE |
                                              PRJ_NOTIFY_FILE_HANDLE_CLOSED_FILE_DELETED |
                                              PRJ_NOTIFY_FILE_RENAMED;

// Override all notifications for C:\VirtRoot\foo\subdir1 and its descendants.
notificationMappings[2].NotificationRoot = L"foo\\subdir1";
notificationMappings[2].NotificationBitMask = PRJ_NOTIFY_SUPPRESS_NOTIFICATIONS;

startOpts.NotificationMappings = notificationMappings;
startOpts.NotificationMappingsCount = ARRAYSIZE(notificationMapping);

// Start the instance and provide our notification mappings.
HRESULT hr;
PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT instanceHandle;
hr = PrjStartVirtualizing(rootName,
                          &callbackTable,
                          nullptr,
                          &startOpts,
                          &instanceHandle);
if (FAILED(hr))
{
    wprintf(L"Failed to start the virtualization instance (0x%08x)\n", hr);
    return;
}

Empfangen von Benachrichtigungen

ProjFS sendet Benachrichtigungen über Dateisystemvorgänge, indem der PRJ_NOTIFICATION_CB-Rückruf des Anbieters aufgerufen wird. Dies gilt für jeden Vorgang, für den sich der Anbieter registriert hat. Wenn, wie im obigen Beispiel, der Anbieter für PRJ_NOTIFY_NEW_FILE_CREATED | PRJ_NOTIFY_FILE_OPENED | PRJ_NOTIFY_PRE_DELETE | PRJ_NOTIFY_FILE_HANDLE_CLOSED_FILE_DELETED | PRJ_NOTIFY_FILE_RENAMED registriert ist und eine Anwendung eine neue Datei im Benachrichtigungsstamm erstellt hat, ruft ProjFS den PRJ_NOTIFICATION_CB-Rückruf mit dem Pfadnamen der neuen Datei und dem auf PRJ_NOTIFICATION_NEW_FILE_CREATED festgelegten Benachrichtigungsparameter auf.

ProjFS sendet die Benachrichtigungen in der folgenden Liste, bevor der zugeordnete Dateisystemvorgang stattfindet. Wenn der Anbieter einen Fehlercode aus dem PRJ_NOTIFICATION_CB-Rückruf zurückgibt, schlägt der Dateisystemvorgang fehl und gibt den vom Anbieter angegebenen Fehlercode zurück.

  • PRJ_NOTIFICATION_PRE_DELETE – Die Datei wird gelöscht.
  • PRJ_NOTIFICATION_PRE_RENAME – Die Datei wird umbenannt.
  • PRJ_NOTIFICATION_PRE_SET_HARDLINK – Es wird ein harter Link für die Datei erstellt.
  • PRJ_NOTIFICATION_FILE_PRE_CONVERT_TO_FULL – Eine Datei wird von einem Platzhalter auf eine vollständige Datei erweitert, was darauf hinweist, dass der Inhalt wahrscheinlich geändert wird.

ProjFS sendet die Benachrichtigungen in der folgenden Liste, nachdem der zugeordnete Dateisystemvorgang abgeschlossen wurde.

  • PRJ_NOTIFICATION_FILE_OPENED – Eine vorhandene Datei wurde geöffnet.
    • Obwohl diese Benachrichtigung nach dem Öffnen der Datei gesendet wird, kann der Anbieter einen Fehlercode zurückgeben. Als Antwort bricht ProjFS das Öffnen ab und gibt den Fehler an den Aufrufer zurück.
  • PRJ_NOTIFICATION_NEW_FILE_CREATED – Eine neue Datei wurde erstellt.
  • PRJ_NOTIFICATION_FILE_OVERWRITTEN – Eine vorhandene Datei wurde überschrieben, z. B. durch Aufrufen von CreateFileW mit dem CREATE_ALWAYSdwCreationDisposition-Flag.
  • PRJ_NOTIFICATION_FILE_RENAMED – Eine Datei wurde umbenannt.
  • PRJ_NOTIFICATION_HARDLINK_CREATED – Für eine Datei wurde ein harter Link erstellt.
  • PRJ_NOTIFICATION_FILE_HANDLE_CLOSED_NO_MODIFICATION – Ein Dateihandle wurde geschlossen, und der Inhalt der Datei wurde weder geändert noch gelöscht.
  • PRJ_NOTIFICATION_FILE_HANLDE_CLOSED_FILE_MODIFIED – Ein Dateihandle wurde geschlossen, und der Inhalt der Datei wurde geändert.
  • PRJ_NOTIFICATION_FILE_HANDLE_CLOSED_FILE_DELETED – Ein Dateihandle wurde geschlossen und die Datei wurde beim Schließen des Handles gelöscht.

Weitere Informationen zu jeder Benachrichtigung finden Sie in der Dokumentation für PRJ_NOTIFICATION.

Zum PRJ_NOTIFICATION_CB-Rückruf gehörende notificationParameters-Parameter gibt zusätzliche Parameter für bestimmte Benachrichtigungen an. Wenn ein Anbieter eine Benachrichtigung wie PRJ_NOTIFICATION_FILE_OPENED, PRJ_NOTIFICATION_NEW_FILE_CREATED, PRJ_NOTIFICATION_FILE_OVERWRITTEN oder PRJ_NOTIFICATION_FILE_RENAMED empfängt, kann der Anbieter einen neuen Satz von Benachrichtigungen angeben, die für die Datei empfangen werden sollen. Weitere Informationen finden Sie in der Dokumentation für PRJ_NOTIFICATION_CB.

Das folgende Beispiel zeigt einfache Beispiele dafür, wie ein Anbieter die Benachrichtigungen verarbeiten kann, die er im obigen Codeausschnitt registriert hat.

#include <windows.h>

HRESULT
MyNotificationCallback(
    _In_ const PRJ_CALLBACK_DATA* callbackData,
    _In_ BOOLEAN isDirectory,
    _In_ PRJ_NOTIFICATION notification,
    _In_opt_z_ PCWSTR destinationFileName,
    _Inout_ PRJ_NOTIFICATION_PARAMETERS* operationParameters
    )
{
    HRESULT hr = S_OK;

    switch (notification)
    {
    case PRJ_NOTIFY_NEW_FILE_CREATED:

        wprintf(L"A new %ls was created at [%ls].\n",
                isDirectory ? L"directory" : L"file",
                callbackData->FilePathName);

        // Let's stop getting notifications inside newly-created directories.
        if (isDirectory)
        {
            operationParameters->PostCreate.NotificationMask = PRJ_NOTIFY_SUPPRESS_NOTIFICATIONS;
        }
        break;

    case PRJ_NOTIFY_FILE_OPENED:

        wprintf(L"Handle opened to %ls [%ls].\n",
                isDirectory ? L"directory": L"file",
                callbackData->FilePathName);
        break;

    case PRJ_NOTIFY_PRE_DELETE:

        wprintf(L"Attempt to delete %ls [%ls]: ",
                isDirectory ? L"directory": L"file",
                callbackData->FilePathName);

        // MyAllowDelete is a routine the provider might implement to decide
        // whether to allow a given file/directory to be deleted.
        if (!MyAllowDelete(callbackData->FilePathName, isDirectory))
        {
            wprintf(L"DENIED\n");
            hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
        }
        else
        {
            wprintf(L"ALLOWED\n");
        }
        break;

    case PRJ_NOTIFY_FILE_HANDLE_CLOSED_FILE_DELETED:

        wprintf(L"The %ls [%ls] was deleted.\n",
                isDirectory ? L"directory": L"file",
                callbackData->FilePathName);
        break;

    case PRJ_NOTIFY_FILE_RENAMED:

        if (wcslen(callbackData->FilePathName) == 0)
        {
            // If callbackData->FilePathName is an empty string, then the item
            // that was renamed was originally in a location not under the
            // virtualization root.
            wprintf(L"A %ls was renamed into the virtualization root,\n",
                    isDirectory ? L"directory": L"file");
            wprintf(L"Its new location is [%ls].\n",
                    destinationFileName);
        }
        else if (wcslen(destinationFileName) == 0)
        {
            // If destinationFileName is an empty string, then the item that was
            // renamed is now in a location not under the virtualization root.
            wprintf(L"A %ls was renamed out of the virtualization root.\n",
                    isDirectory ? L"directory": L"file");
            wprintf(L"Its original location was [%ls].\n",
                    callbackData->FilePathName);
        }
        else
        {
            // If both names are specified, both the new and old names are under
            // the virtualization root (it is never the case that nether name is
            // specified).
            wprintf(L"A %ls [%ls] was renamed.\n",
                    isDirectory ? L"directory": L"file",
                    callbackData->FilePathName);
            wprintf(L"Its new name is [%ls].\n", destinationFileName);
        }
        break;

    default:
        wprintf(L"Unrecognized notification: 0x%08x");
    }

    // Note that this may be a failure code from MyAllowDelete().
    return hr;
}