Starting with Windows Vista, you must ensure that APCs are not disabled before calling this routine. The KeAreAllApcsDisabled routine can be used to verify that APCs are not disabled.
VOID
NTAPI
KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame)
{
PKTHREAD Thread = KeGetCurrentThread();
PKPROCESS Process = Thread->ApcState.Process;
PKTRAP_FRAME OldTrapFrame;
PLIST_ENTRY ApcListEntry;
PKAPC Apc;
KLOCK_QUEUE_HANDLE ApcLock;
PKKERNEL_ROUTINE KernelRoutine;
PVOID NormalContext;
PKNORMAL_ROUTINE NormalRoutine;
PVOID SystemArgument1;
PVOID SystemArgument2;
ASSERT_IRQL_EQUAL(APC_LEVEL);
/* Save the old trap frame and set current one */
OldTrapFrame = Thread->TrapFrame;
Thread->TrapFrame = TrapFrame;
/* Clear Kernel APC Pending */
Thread->ApcState.KernelApcPending = FALSE;
/* Check if Special APCs are disabled */
if (Thread->SpecialApcDisable) goto Quickie; // 总开关,如果SpecialApcDisable那么整个线程的APC都不会被执行
/* Do the Kernel APCs first */
while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
{
/* Lock the APC Queue */
KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
/* Check if the list became empty now */
if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
{
/* It is, release the lock and break out */
KiReleaseApcLock(&ApcLock);
break;
}
/* Kernel APC is not pending anymore */
Thread->ApcState.KernelApcPending = FALSE;
/* Get the next Entry */
ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
/* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
NormalRoutine = Apc->NormalRoutine;
KernelRoutine = Apc->KernelRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
/* Special APC */
if (!NormalRoutine)
{
/* Remove the APC from the list */
RemoveEntryList(ApcListEntry);
Apc->Inserted = FALSE;
/* Release the APC lock */
KiReleaseApcLock(&ApcLock);
/* Call the Special APC */
KernelRoutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
/* Make sure it returned correctly */
if (KeGetCurrentIrql() != ApcLock.OldIrql)
{
KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
(KeGetCurrentIrql() << 16) |
(ApcLock.OldIrql << 8),
(ULONG_PTR)KernelRoutine,
(ULONG_PTR)Apc,
(ULONG_PTR)NormalRoutine);
}
}
else
{
/* Normal Kernel APC, make sure it's safe to deliver */
if ((Thread->ApcState.KernelApcInProgress) ||
(Thread->KernelApcDisable)) // 子开关,控制着Normal KernelApc的执行与否
{
/* Release lock and return */
KiReleaseApcLock(&ApcLock);
goto Quickie;
}
/* Dequeue the APC */
RemoveEntryList(ApcListEntry);
Apc->Inserted = FALSE;
/* Go back to APC_LEVEL */
KiReleaseApcLock(&ApcLock);
/* Call the Kernel APC */
KernelRoutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
/* Make sure it returned correctly */
if (KeGetCurrentIrql() != ApcLock.OldIrql)
{
KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
(KeGetCurrentIrql() << 16) |
(ApcLock.OldIrql << 8),
(ULONG_PTR)KernelRoutine,
(ULONG_PTR)Apc,
(ULONG_PTR)NormalRoutine);
}
/* Check if there still is a Normal Routine */
if (NormalRoutine)
{
/* At Passive Level, an APC can be prempted by a Special APC */
Thread->ApcState.KernelApcInProgress = TRUE;
KeLowerIrql(PASSIVE_LEVEL);
/* Call and Raise IRQL back to APC_LEVEL */
NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
KeRaiseIrql(APC_LEVEL, &ApcLock.OldIrql);
}
/* Set Kernel APC in progress to false and loop again */
Thread->ApcState.KernelApcInProgress = FALSE;
}
}
/* Now we do the User APCs */
if ((DeliveryMode == UserMode) &&
!(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
(Thread->ApcState.UserApcPending))
{
/* Lock the APC Queue */
KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
/* It's not pending anymore */
Thread->ApcState.UserApcPending = FALSE;
/* Check if the list became empty now */
if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))
{
/* It is, release the lock and break out */
KiReleaseApcLock(&ApcLock);
goto Quickie;
}
/* Get the actual APC object */
ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
/* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
NormalRoutine = Apc->NormalRoutine;
KernelRoutine = Apc->KernelRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
/* Remove the APC from Queue, and release the lock */
RemoveEntryList(ApcListEntry);
Apc->Inserted = FALSE;
KiReleaseApcLock(&ApcLock);
/* Call the kernel routine */
KernelRoutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
/* Check if there's no normal routine */
if (!NormalRoutine)
{
/* Check if more User APCs are Pending */
KeTestAlertThread(UserMode);
}
else
{
/* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
KiInitializeUserApc(ExceptionFrame,
TrapFrame,
NormalRoutine,
NormalContext,
SystemArgument1,
SystemArgument2);
}
}
Quickie:
/* Make sure we're still in the same process */
if (Process != Thread->ApcState.Process)
{
/* Erm, we got attached or something! BAD! */
KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
(ULONG_PTR)Process,
(ULONG_PTR)Thread->ApcState.Process,
Thread->ApcStateIndex,
KeGetCurrentPrcb()->DpcRoutineActive);
}
/* Restore the trap frame */
Thread->TrapFrame = OldTrapFrame;
}
An asynchronous procedure call (APC) is a function that executes asynchronously. APCs are similar to deferred procedure calls (DPCs), but unlike DPCs, APCs execute within the context of a particular thread. Drivers (other than file systems and file-system filter drivers) do not use APCs directly, but other parts of the operating system do, so you need to be aware of how APCs work.
The Windows operating system uses three kinds of APCs:
1. User APCs run strictly in user mode and only when the current thread is in an alertable wait state. The operating system uses user APCs to implement mechanisms such as overlapped I/O and the QueueUserApc Win32 routine.
2. Normal kernel APCs run in kernel mode at IRQL = PASSIVE_LEVEL. A normal kernel APC preempts all user-mode code, including user APCs. Normal kernel APCs are generally used by file systems and file-system filter drivers.
3. Special kernel APCs run in kernel mode at IRQL = APC_LEVEL. A special kernel APC preempts user-mode code and kernel-mode code that executes at IRQL = PASSIVE_LEVEL, including both user APCs and normal kernel APCs. The operating system uses special kernel APCs to handle operations such as I/O request completion.
VOID
NTAPI
KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame)
{
PKTHREAD Thread = KeGetCurrentThread();
PKPROCESS Process = Thread->ApcState.Process;
PKTRAP_FRAME OldTrapFrame;
PLIST_ENTRY ApcListEntry;
PKAPC Apc;
KLOCK_QUEUE_HANDLE ApcLock;
PKKERNEL_ROUTINE KernelRoutine;
PVOID NormalContext;
PKNORMAL_ROUTINE NormalRoutine;
PVOID SystemArgument1;
PVOID SystemArgument2;
ASSERT_IRQL_EQUAL(APC_LEVEL);
/* Save the old trap frame and set current one */
OldTrapFrame = Thread->TrapFrame;
Thread->TrapFrame = TrapFrame;
/* Clear Kernel APC Pending */
Thread->ApcState.KernelApcPending = FALSE;
/* Check if Special APCs are disabled */
if (Thread->SpecialApcDisable) goto Quickie; // 总开关,如果SpecialApcDisable那么整个线程的APC都不会被执行
/* Do the Kernel APCs first */
while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
{
/* Lock the APC Queue */
KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
/* Check if the list became empty now */
if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
{
/* It is, release the lock and break out */
KiReleaseApcLock(&ApcLock);
break;
}
/* Kernel APC is not pending anymore */
Thread->ApcState.KernelApcPending = FALSE;
/* Get the next Entry */
ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
/* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
NormalRoutine = Apc->NormalRoutine;
KernelRoutine = Apc->KernelRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
/* Special APC */
if (!NormalRoutine)
{
/* Remove the APC from the list */
RemoveEntryList(ApcListEntry);
Apc->Inserted = FALSE;
/* Release the APC lock */
KiReleaseApcLock(&ApcLock);
/* Call the Special APC */
KernelRoutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
/* Make sure it returned correctly */
if (KeGetCurrentIrql() != ApcLock.OldIrql)
{
KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
(KeGetCurrentIrql() << 16) |
(ApcLock.OldIrql << 8),
(ULONG_PTR)KernelRoutine,
(ULONG_PTR)Apc,
(ULONG_PTR)NormalRoutine);
}
}
else
{
/* Normal Kernel APC, make sure it's safe to deliver */
if ((Thread->ApcState.KernelApcInProgress) ||
(Thread->KernelApcDisable)) // 子开关,控制着Normal KernelApc的执行与否
{
/* Release lock and return */
KiReleaseApcLock(&ApcLock);
goto Quickie;
}
/* Dequeue the APC */
RemoveEntryList(ApcListEntry);
Apc->Inserted = FALSE;
/* Go back to APC_LEVEL */
KiReleaseApcLock(&ApcLock);
/* Call the Kernel APC */
KernelRoutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
/* Make sure it returned correctly */
if (KeGetCurrentIrql() != ApcLock.OldIrql)
{
KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
(KeGetCurrentIrql() << 16) |
(ApcLock.OldIrql << 8),
(ULONG_PTR)KernelRoutine,
(ULONG_PTR)Apc,
(ULONG_PTR)NormalRoutine);
}
/* Check if there still is a Normal Routine */
if (NormalRoutine)
{
/* At Passive Level, an APC can be prempted by a Special APC */
Thread->ApcState.KernelApcInProgress = TRUE;
KeLowerIrql(PASSIVE_LEVEL);
/* Call and Raise IRQL back to APC_LEVEL */
NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
KeRaiseIrql(APC_LEVEL, &ApcLock.OldIrql);
}
/* Set Kernel APC in progress to false and loop again */
Thread->ApcState.KernelApcInProgress = FALSE;
}
}
/* Now we do the User APCs */
if ((DeliveryMode == UserMode) &&
!(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
(Thread->ApcState.UserApcPending))
{
/* Lock the APC Queue */
KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
/* It's not pending anymore */
Thread->ApcState.UserApcPending = FALSE;
/* Check if the list became empty now */
if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))
{
/* It is, release the lock and break out */
KiReleaseApcLock(&ApcLock);
goto Quickie;
}
/* Get the actual APC object */
ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
/* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
NormalRoutine = Apc->NormalRoutine;
KernelRoutine = Apc->KernelRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
/* Remove the APC from Queue, and release the lock */
RemoveEntryList(ApcListEntry);
Apc->Inserted = FALSE;
KiReleaseApcLock(&ApcLock);
/* Call the kernel routine */
KernelRoutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
/* Check if there's no normal routine */
if (!NormalRoutine)
{
/* Check if more User APCs are Pending */
KeTestAlertThread(UserMode);
}
else
{
/* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
KiInitializeUserApc(ExceptionFrame,
TrapFrame,
NormalRoutine,
NormalContext,
SystemArgument1,
SystemArgument2);
}
}
Quickie:
/* Make sure we're still in the same process */
if (Process != Thread->ApcState.Process)
{
/* Erm, we got attached or something! BAD! */
KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
(ULONG_PTR)Process,
(ULONG_PTR)Thread->ApcState.Process,
Thread->ApcStateIndex,
KeGetCurrentPrcb()->DpcRoutineActive);
}
/* Restore the trap frame */
Thread->TrapFrame = OldTrapFrame;
}
An asynchronous procedure call (APC) is a function that executes asynchronously. APCs are similar to deferred procedure calls (DPCs), but unlike DPCs, APCs execute within the context of a particular thread. Drivers (other than file systems and file-system filter drivers) do not use APCs directly, but other parts of the operating system do, so you need to be aware of how APCs work.
The Windows operating system uses three kinds of APCs:
1. User APCs run strictly in user mode and only when the current thread is in an alertable wait state. The operating system uses user APCs to implement mechanisms such as overlapped I/O and the QueueUserApc Win32 routine.
2. Normal kernel APCs run in kernel mode at IRQL = PASSIVE_LEVEL. A normal kernel APC preempts all user-mode code, including user APCs. Normal kernel APCs are generally used by file systems and file-system filter drivers.
3. Special kernel APCs run in kernel mode at IRQL = APC_LEVEL. A special kernel APC preempts user-mode code and kernel-mode code that executes at IRQL = PASSIVE_LEVEL, including both user APCs and normal kernel APCs. The operating system uses special kernel APCs to handle operations such as I/O request completion.
VOID
NTAPI
KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame)
{
PKTHREAD Thread = KeGetCurrentThread();
PKPROCESS Process = Thread->ApcState.Process;
PKTRAP_FRAME OldTrapFrame;
PLIST_ENTRY ApcListEntry;
PKAPC Apc;
KLOCK_QUEUE_HANDLE ApcLock;
PKKERNEL_ROUTINE KernelRoutine;
PVOID NormalContext;
PKNORMAL_ROUTINE NormalRoutine;
PVOID SystemArgument1;
PVOID SystemArgument2;
ASSERT_IRQL_EQUAL(APC_LEVEL);
/* Save the old trap frame and set current one */
OldTrapFrame = Thread->TrapFrame;
Thread->TrapFrame = TrapFrame;
/* Clear Kernel APC Pending */
Thread->ApcState.KernelApcPending = FALSE;
/* Check if Special APCs are disabled */
if (Thread->SpecialApcDisable) goto Quickie; // 总开关,如果SpecialApcDisable那么整个线程的APC都不会被执行
/* Do the Kernel APCs first */
while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
{
/* Lock the APC Queue */
KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
/* Check if the list became empty now */
if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
{
/* It is, release the lock and break out */
KiReleaseApcLock(&ApcLock);
break;
}
/* Kernel APC is not pending anymore */
Thread->ApcState.KernelApcPending = FALSE;
/* Get the next Entry */
ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
/* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
NormalRoutine = Apc->NormalRoutine;
KernelRoutine = Apc->KernelRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
/* Special APC */
if (!NormalRoutine)
{
/* Remove the APC from the list */
RemoveEntryList(ApcListEntry);
Apc->Inserted = FALSE;
/* Release the APC lock */
KiReleaseApcLock(&ApcLock);
/* Call the Special APC */
KernelRoutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
/* Make sure it returned correctly */
if (KeGetCurrentIrql() != ApcLock.OldIrql)
{
KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
(KeGetCurrentIrql() << 16) |
(ApcLock.OldIrql << 8),
(ULONG_PTR)KernelRoutine,
(ULONG_PTR)Apc,
(ULONG_PTR)NormalRoutine);
}
}
else
{
/* Normal Kernel APC, make sure it's safe to deliver */
if ((Thread->ApcState.KernelApcInProgress) ||
(Thread->KernelApcDisable)) // 子开关,控制着Normal KernelApc的执行与否
{
/* Release lock and return */
KiReleaseApcLock(&ApcLock);
goto Quickie;
}
/* Dequeue the APC */
RemoveEntryList(ApcListEntry);
Apc->Inserted = FALSE;
/* Go back to APC_LEVEL */
KiReleaseApcLock(&ApcLock);
/* Call the Kernel APC */
KernelRoutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
/* Make sure it returned correctly */
if (KeGetCurrentIrql() != ApcLock.OldIrql)
{
KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
(KeGetCurrentIrql() << 16) |
(ApcLock.OldIrql << 8),
(ULONG_PTR)KernelRoutine,
(ULONG_PTR)Apc,
(ULONG_PTR)NormalRoutine);
}
/* Check if there still is a Normal Routine */
if (NormalRoutine)
{
/* At Passive Level, an APC can be prempted by a Special APC */
Thread->ApcState.KernelApcInProgress = TRUE;
KeLowerIrql(PASSIVE_LEVEL);
/* Call and Raise IRQL back to APC_LEVEL */
NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
KeRaiseIrql(APC_LEVEL, &ApcLock.OldIrql);
}
/* Set Kernel APC in progress to false and loop again */
Thread->ApcState.KernelApcInProgress = FALSE;
}
}
/* Now we do the User APCs */
if ((DeliveryMode == UserMode) &&
!(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
(Thread->ApcState.UserApcPending))
{
/* Lock the APC Queue */
KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
/* It's not pending anymore */
Thread->ApcState.UserApcPending = FALSE;
/* Check if the list became empty now */
if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))
{
/* It is, release the lock and break out */
KiReleaseApcLock(&ApcLock);
goto Quickie;
}
/* Get the actual APC object */
ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
/* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
NormalRoutine = Apc->NormalRoutine;
KernelRoutine = Apc->KernelRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
/* Remove the APC from Queue, and release the lock */
RemoveEntryList(ApcListEntry);
Apc->Inserted = FALSE;
KiReleaseApcLock(&ApcLock);
/* Call the kernel routine */
KernelRoutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
/* Check if there's no normal routine */
if (!NormalRoutine)
{
/* Check if more User APCs are Pending */
KeTestAlertThread(UserMode);
}
else
{
/* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
KiInitializeUserApc(ExceptionFrame,
TrapFrame,
NormalRoutine,
NormalContext,
SystemArgument1,
SystemArgument2);
}
}
Quickie:
/* Make sure we're still in the same process */
if (Process != Thread->ApcState.Process)
{
/* Erm, we got attached or something! BAD! */
KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
(ULONG_PTR)Process,
(ULONG_PTR)Thread->ApcState.Process,
Thread->ApcStateIndex,
KeGetCurrentPrcb()->DpcRoutineActive);
}
/* Restore the trap frame */
Thread->TrapFrame = OldTrapFrame;
}
An asynchronous procedure call (APC) is a function that executes asynchronously. APCs are similar to deferred procedure calls (DPCs), but unlike DPCs, APCs execute within the context of a particular thread. Drivers (other than file systems and file-system filter drivers) do not use APCs directly, but other parts of the operating system do, so you need to be aware of how APCs work.
The Windows operating system uses three kinds of APCs:
1. User APCs run strictly in user mode and only when the current thread is in an alertable wait state. The operating system uses user APCs to implement mechanisms such as overlapped I/O and the QueueUserApc Win32 routine.
2. Normal kernel APCs run in kernel mode at IRQL = PASSIVE_LEVEL. A normal kernel APC preempts all user-mode code, including user APCs. Normal kernel APCs are generally used by file systems and file-system filter drivers.
3. Special kernel APCs run in kernel mode at IRQL = APC_LEVEL. A special kernel APC preempts user-mode code and kernel-mode code that executes at IRQL = PASSIVE_LEVEL, including both user APCs and normal kernel APCs. The operating system uses special kernel APCs to handle operations such as I/O request completion.
An asynchronous procedure call (APC) is a function that executes asynchronously. APCs are similar to deferred procedure calls (DPCs), but unlike DPCs, APCs execute within the context of a particular thread. Drivers (other than file systems and file-system filter drivers) do not use APCs directly, but other parts of the operating system do, so you need to be aware of how APCs work.
The Windows operating system uses three kinds of APCs:
1. User APCs run strictly in user mode and only when the current thread is in an alertable wait state. The operating system uses user APCs to implement mechanisms such as overlapped I/O and the QueueUserApc Win32 routine.
https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntddk/nc-ntddk-pload_image_notify_routine 就有说明: he operating system calls the driver's load-image notify routine at PASSIVE_LEVEL inside a critical region with normal kernel APCs always disabled and sometimes with both kernel and special APCs disabled.