Share via


程式設計考量

網路模組應該使用某種形式的參考計數,來追蹤連結網路模組的網路 程式設計介面 (NPI) 函式的進行中呼叫數目。 當兩個網路模組的其中一個與 NMR 取消註冊時,這可協助中斷連結網路模組的中斷連結。 除非沒有連結網路模組 NPI 函式的進行中呼叫,否則網路模組無法完成中斷連結。 網路模組也必須確保一旦中斷連結網路模組之後,就不會再起始對先前連結的網路模組呼叫。

例如,用戶端模組可能會使用類似下列的實作來追蹤附加提供者模組 NPI 函式的進行中呼叫數目:

// Context structure for the client's binding to a provider module
typedef struct CLIENT_BINDING_CONTEXT_ {
  LIST_ENTRY Link;
  HANDLE NmrBindingHandle;
  PVOID ProviderBindingContext;
  PEXNPI_PROVIDER_DISPATCH ProviderDispatch;
  KSPIN_LOCK DetachLock;
  LONG InProgressCallCount;
  LONG Detaching;
  .
  . // Other client-specific members
  .
} CLIENT_BINDING_CONTEXT, *PCLIENT_BINDING_CONTEXT;

// Pool tag used for allocating the binding context
#define BINDING_CONTEXT_POOL_TAG 'tpcb'

// Structure for the client's dispatch table
const EXNPI_CLIENT_DISPATCH Dispatch = {
  .
  . // Function pointers to the client module's
  . // NPI callback functions
  .
};

// Head of linked list of binding context structures
LIST_ENTRY BindingContextList;

// Spin lock for binding context list
KSPIN_LOCK BindingContextListLock;

// Prototype for the client module's unload function
VOID
  Unload(
    PDRIVER_OBJECT DriverObject
    );

// Variable to contain the handle for the registration
HANDLE ClientHandle;

// DriverEntry function
NTSTATUS
  DriverEntry(
    PDRIVER_OBJECT DriverObject,
    PUNICODE_STRING RegistryPath
    )
{
  NTSTATUS Status;

  // Specify the unload function
  DriverObject->DriverUnload = Unload;

  // Initialize the binding context list spin lock
  KeInitializeSpinLock(
    &BindingContextListLock
    );

  // Initialize the binding context list head
  InitializeListHead(
    &BindingContextList
    );

  .
  . // Other initialization tasks
  .

  // Register the client module with the NMR
  Status = NmrRegisterClient(
    &ClientCharacteristics,
    &ClientRegistrationContext,
    &ClientHandle,
    );

  // Return the result of the registration
  return Status;
}

// ClientAttachProvider callback function
NTSTATUS
  ClientAttachProvider(
    IN HANDLE NmrBindingHandle,
    IN PVOID ClientContext,
    IN PNPI_REGISTRATION_INSTANCE ProviderRegistrationInstance
    )
{
  PNPI_MODULEID ProviderModuleId;
  PEXNPI_PROVIDER_CHARACTERISTICS ProviderNpiSpecificCharacteristics;
  PCLIENT_BINDING_CONTEXT BindingContext;
  PVOID ProviderBindingContext;
  PEXNPI_PROVIDER_DISPATCH ProviderDispatch;
  KLOCK_QUEUE_HANDLE BindingContextListLockHandle;
  NTSTATUS Status;

  // Get pointers to the provider module's identification structure
  // and the provider module's NPI-specific characteristics structure
  ProviderModuleId = ProviderRegistrationInstance->ModuleId;
  ProviderNpiSpecificCharacteristics =
    (PEXNPI_PROVIDER_CHARACTERISTICS)
      ProviderRegistrationInstance->NpiSpecificCharacteristics;

  //
  // Use the data in the structures pointed to by
  // ProviderRegistrationInstance, ProviderModuleId,
  // and ProviderNpiSpecificCharacteristics to determine
  // whether to attach to the provider module.
  //

  // If the client module determines that it will not attach
  // to the provider module
  if (...)
  {
    // Return status code indicating the modules did not
    // attach to each other
    return STATUS_NOINTERFACE;
  }

  // Allocate memory for the client's binding context structure
  BindingContext =
    (PCLIENT_BINDING_CONTEXT)
      ExAllocatePoolWithTag(
        NonPagedPool,
        sizeof(CLIENT_BINDING_CONTEXT),
        BINDING_CONTEXT_POOL_TAG
        );

  // Check result of allocation
  if (BindingContext == NULL)
  {
    // Return error status code
    return STATUS_INSUFFICIENT_RESOURCES;
  }

  // Initialize the client binding context structure
  KeInitializeSpinLock(
    &BindingContext->DetachLock
   );
  BindingContext->InProgressCallCount = 0;
  BindingContext->Detaching = 0;
  ...

  // Continue with the attachment to the provider module
  Status = NmrClientAttachProvider(
    NmrBindingHandle,
    BindingContext,
    &Dispatch,
    &ProviderBindingContext,
    &ProviderDispatch
    );

  // Check result of attachment
  if (Status == STATUS_SUCCESS)
  {
    // Save NmrBindingHandle, ProviderBindingContext,
    // and ProviderDispatch for future reference
    BindingContext->NmrBindingHandle =
      NmrBindingHandle;
    BindingContext->ProviderBindingContext =
      ProviderBindingContext;
    BindingContext->ProviderDispatch =
      ProviderDispatch;

    // Acquire the binding context list spin lock
    KeAcquireInStackQueuedSpinLock(
      &BindingContextListLock,
      &BindingContextListLockHandle
      );

    // Add this binding context to the list of valid
    // binding contexts
    InsertTailList(
      &BindingContextList,
      &BindingContext->Link
      );
 
    // Release the binding context list spin lock
    KeReleaseInStackQueuedSpinLock(
      &BindingContextListLockHandle
      );
  }

  // Attachment did not succeed
  else
  {
    // Free memory for client's binding context structure
    ExFreePoolWithTag(
      BindingContext,
      BINDING_CONTEXT_POOL_TAG
      );
  }

  // Return result of attachment
  return Status;
}

// Wrapper function around a provider NPI function
//
// Each of the provider NPI functions should be wrapped
// in this manner.
NTSTATUS
  ProviderNpiFunctionXxx(
    ClientBindingContext,
    .
    . // Parameters to the provider NPI function
    .
    )
{
  KLOCK_QUEUE_HANDLE BindingContextListLockHandle;
  KLOCK_QUEUE_HANDLE DetachLockHandle;
  PCLIENT_BINDING_CONTEXT BindingContextListElement;
  PLIST_ENTRY Entry;
  NTSTATUS Status;

  // Acquire the binding context list spin lock
  KeAcquireInStackQueuedSpinLock(
    &BindingContextListLock,
    &BindingContextListLockHandle
    );

  // Search for the binding context in the list of valid
  // binding contexts
  for (Entry = BindingContextList.Flink;
       Entry != &BindingContextList;
       Entry = Entry->Flink)
  {
    // Get the next binding context from the list
    BindingContextListElement =
      CONTAINING_RECORD(
        Entry,
        CLIENT_BINDING_CONTEXT,
        Link
        );

    // Check if this binding context is a match
    if (BindingContextListElement == ClientBindingContext)
    {
      // Break out of the search loop
      break;
    }
  }

  // Check if the binding context was not found
  if (Entry == &BindingContextList)
  {
    // Release the binding context list spin lock
    KeReleaseInStackQueuedSpinLock(
      &BindingContextListLockHandle
      );

    // Return status indicating that the interface is not available
    return STATUS_NOINTERFACE;
  }

  // Acquire the detach spin lock at DPC level
  KeAcquireInStackQueuedSpinLockAtDpcLevel(
    &ClientBindingContext->DetachLock,
    &DetachLockHandle
    );

  // The modules should not be detaching
  ASSERT(ClientBindingContext->Detaching != 1);

  // Increment the in-progress call count
  ClientBindingContext->InProgressCallCount++;

  // Release the detach spin lock from DPC level
  KeReleaseInStackQueuedSpinLockFromDpcLevel(
    &DetachLockHandle
    );

  // Release the binding context list spin lock
  KeReleaseInStackQueuedSpinLock(
    &BindingContextListLockHandle
    );

  // Call the provider NPI function
  Status =
    ClientBindingContext->ProviderDispatch->ProviderNpiFunctionXxx(
      ClientBindingContext->ProviderBindingContext,
      .
      . // Parameters to the provider NPI function
      .
      );

  // Check if pending
  if (Status == STATUS_PENDING)
  {
    // If completion of the call is pending, then when the call
    // completes, the completion routine (or other callback function
    // that is called upon completion of the call) must include the
    // the same code as below for the non-pending case.

    // Return pending status
    return STATUS_PENDING;
  }

  // Acquire the detach spin lock
  KeAcquireInStackQueuedSpinLock(
    &ClientBindingContext->DetachLock,
    &DetachLockHandle
    );

  // Decrement the in-progress call count
  ClientBindingContext->InProgressCallCount--;

  // Check if the modules are now detaching
  if (ClientBindingContext->Detaching == 1)
  {
    // Check if this call was the last of the in-progress
    // calls to the provider module to be completed
    if (ClientBindingContext->InProgressCallCount == 0)
    {
      // Release the detach spin lock
      KeReleaseInStackQueuedSpinLock(
        &DetachLockHandle
        );

      // Inform the NMR that detachment is complete
      NmrClientDetachProviderComplete(
        ClientBindingContext->NmrBindingHandle
        );
    }
    else
    {
      // Release the detach spin lock
      KeReleaseInStackQueuedSpinLock(
        &DetachLockHandle
        );
    }
  }
  else
  {
    // Release the detach spin lock
    KeReleaseInStackQueuedSpinLock(
      &DetachLockHandle
      );
  }

  // Return status of the call to the provider NPI function
  return Status;
}

// ClientDetachProvider callback function
NTSTATUS
  ClientDetachProvider(
    IN PVOID ClientBindingContext
    )
{
  PCLIENT_BINDING_CONTEXT BindingContext;
  KLOCK_QUEUE_HANDLE BindingContextListLockHandle;
  KLOCK_QUEUE_HANDLE DetachLockHandle;
  NTSTATUS Status;

  // Get a pointer to the binding context
  BindingContext = (PCLIENT_BINDING_CONTEXT)ClientBindingContext;

  // Acquire the binding context list spin lock
  KeAcquireInStackQueuedSpinLock(
    &BindingContextListLock,
    &BindingContextListLockHandle
    );

  // Remove the binding context from the binding context list
  RemoveEntryList(&BindingContext->Link);

  // Acquire the detach spin lock at DPC level
  KeAcquireInStackQueuedSpinLockAtDpcLevel(
    &BindingContext->DetachLock,
    &DetachLockHandle
    );

  // Set the flag indicating that the client module is detaching
  // from the provider module so that the completion of the final
  // call will complete the detachment
  BindingContext->Detaching = 1;

  // Check if there are no in-progress NPI function calls to the
  // provider module
  if (BindingContext->InProgressCallCount == 0)
  {
    // Set the status to success to indicate detachment is complete
    Status = STATUS_SUCCESS;
  }

  // There are one or more in-progress NPI function calls
  // to the provider module
  else
  {
    // Set the status to pending to indicate that detachment is
    // pending completion of the in-progress NPI function calls
    Status = STATUS_PENDING;
  }

  // Release the detach spin lock from DPC level
  KeReleaseInStackQueuedSpinLockFromDpcLevel(
    &DetachLockHandle
    );

  // Release the binding context list spin lock
  KeReleaseInStackQueuedSpinLock(
    &BindingContextListLockHandle
    );

  // Return the status of the detachment
  return Status;
}

同樣地,提供者模組可能會使用與上述用戶端模組相同行的實作,以追蹤附加用戶端模組 NPI 回呼函式的進行中呼叫數目。

注意 上述程式碼範例示範一個可能的方法來追蹤連結網路模組 NPI 函式的進行中呼叫數目。 網路模組可能會根據網路模組所支援之特定 NPI 的實作詳細資料,使用替代方法。