บทนำ
งานที่ดำเนินงานบ่อยที่สุดในโปรแกรมควบคุมรุ่นของโปรแกรมควบคุม Windows (WDM) อย่างใดอย่างหนึ่งคือส่งแพคเก็ตการร้องขอการอินพุต/เอาท์พุต (IRPs) จากโปรแกรมควบคุมที่หนึ่งไปยังโปรแกรมควบคุมอื่น ไดรเวอร์อย่างใดอย่างหนึ่ง IRP ของตนเองในการสร้าง และส่งไปยังโปรแกรมควบคุมที่ต่ำกว่า หรือโปรแกรมควบคุมที่ส่งต่อ IRPs ที่ได้รับจากโปรแกรมควบคุมอื่นที่มีแนบข้างต้น
บทความนี้อธิบายวิธีเป็นไปได้ทั้งหมดที่โปรแกรมควบคุมสามารถส่ง IRPs ไปยังโปรแกรมควบคุมที่ต่ำกว่าด้วยโค้ดตัวอย่าง annotated
ทั้งนี้ขึ้นอยู่กับความต้อง ผู้เขียนโปรแกรมควบคุมสามารถทำตามแบบที่กำหนดไว้ในบทความนี้อย่างใดอย่างหนึ่ง และไม่ได้รับผลกระทบ โดย IRP เก่าที่กฎการจัดการ
ส่วนที่ 1 ของหัวข้อนี้แสดงสถานการณ์ 5 เกี่ยวกับวิธีการส่งต่อ IRP การให้โปรแกรมควบคุมอื่นจากขั้นตอนการส่ง และสถานการณ์ 7 คงเหลือที่ (แสดงในส่วนที่ 2 ของชื่อเรื่องนี้) กล่าวถึงวิธีการต่าง ๆ ของ IRP มีการสร้าง และส่งไปยังโปรแกรมควบคุมอื่น ส่วนที่ 2 ของชื่อเรื่องนี้อยู่ในบทความต่อไปนี้ของ Knowledge Base:
326315
(http://support.microsoft.com/kb/326315/
)
วิธีการต่าง ๆ ของการจัดการ IRPs - แผ่นงาน cheat (ส่วนที่ 2 จาก 2)
ก่อนที่คุณตรวจสอบในสถานการณ์ต่าง ๆ หมายเหตุเกี่ยวกับสถานะที่ถูกส่งกลับ โดยตามปกติเสร็จสมบูรณ์ต่อไปนี้:
ขั้นตอนการดำเนินการเสร็จสมบูรณ์ IRP สามารถกลับ STATUS_MORE_PROCESSING_REQUIRED หรือ STATUS_SUCCESS
ตัวจัดการทราน I/O ใช้กฎต่อไปนี้เมื่อโปรแกรมตรวจสอบสถานะ:
- ถ้าสถานะเป็น STATUS_MORE_PROCESSING_REQUIRED หยุดทำ IRP ออกจากตำแหน่งที่ตั้งของกองซ้อนที่เทอร์ และส่งคืน
- ถ้าสถานะเป็นสิ่งอื่นที่ไม่ใช่ STATUS_MORE_PROCESSING_REQUIRED ทำการดำเนินการ IRP upward
เนื่องจากตัวจัดการ I/O ที่ไม่มีการทราบว่ามีใช้ค่าที่ไม่ใช่ STATUS_MORE_PROCESSING_REQUIRED ใช้ STATUS_SUCCESS (เนื่องจากค่า 0 อยู่ได้อย่างมีประสิทธิภาพ loadable architectures ตัวประมวลผลส่วนใหญ่)
เมื่อต้องการปรับปรุง readability ของรหัส Windows XP SP1 และ Windows XP .NET ไดรเวอร์พัฒนา Kit Ntddk.h และ Wdm.h ส่วนหัวของแฟ้มจะมีใหม่
เลขที่กำหนดที่มีชื่อ STATUS_CONTINUE_COMPLETION ซึ่งเป็นแฝงเพื่อ STATUS_SUCCESS ดังที่แสดงในรหัสต่อไปนี้:
//
// This value should be returned from completion routines to continue
// completing the IRP upwards. Otherwise, STATUS_MORE_PROCESSING_REQUIRED
// should be returned.
//
#define STATUS_CONTINUE_COMPLETION STATUS_SUCCESS
//
// Completion routines can also use this enumeration instead of status codes.
//
typedef enum _IO_COMPLETION_ROUTINE_RESULT {
ContinueCompletion = STATUS_CONTINUE_COMPLETION,
StopCompletion = STATUS_MORE_PROCESSING_REQUIRED
} IO_COMPLETION_ROUTINE_RESULT, *PIO_COMPLETION_ROUTINE_RESULT;
สถานการณ์ที่ 1: ส่งต่อ และลืม
ใช้รหัสดังต่อไปนี้หากไดรเวอร์เพียงแค่ต้องการส่งต่อการ IRP ลง และดำเนินการไม่มีการกระทำเพิ่มเติม โปรแกรมควบคุมที่ไม่มีการตั้งค่าขั้นตอนการดำเนินการเสร็จสมบูรณ์ในกรณีนี้ หากไดรเวอร์เป็นไดรเวอร์ระดับบนสุด IRP สามารถให้สมบูรณ์ synchronously หรือ asynchronously ขึ้นอยู่กับสถานะที่ถูกส่งกลับ โดยโปรแกรมควบคุมที่ต่ำลง
NTSTATUS
DispatchRoutine_1(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
//
// You are not setting a completion routine, so just skip the stack
// location because it provides better performance.
//
IoSkipCurrentIrpStackLocation (Irp);
return IoCallDriver(TopOfDeviceStack, Irp);
} สถานการณ์ที่ 2: ส่งต่อ และรอ
ใช้รหัสดังต่อไปนี้หากโปรแกรมควบคุมที่ต้องการส่งต่อ IRP โปรแกรมควบคุมที่ต่ำกว่า และรอให้กลับเพื่อให้สามารถประมวลผลการ IRP ซึ่งมักจะดำเนินการจัดการ IRPs PNP ตัวอย่างเช่น เมื่อคุณได้รับข้อความแสดงแบบ IRP IRP_MN_START_DEVICE คุณต้องการส่งต่อ IRP ลงมาเป็นไดรเวอร์บัส และรอให้เสร็จสมบูรณ์ก่อนที่คุณสามารถเริ่มอุปกรณ์ของคุณ ระบบ Windows XP มีฟังก์ชันใหม่ที่ชื่อว่า
IoForwardIrpSynchronouslyให้คุณสามารถใช้เพื่อทำการดำเนินการนี้ได้อย่างง่ายดาย
NTSTATUS
DispatchRoutine_2(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
KEVENT event;
NTSTATUS status;
KeInitializeEvent(&event, NotificationEvent, FALSE);
//
// You are setting completion routine, so you must copy
// current stack location to the next. You cannot skip a location
// here.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
CompletionRoutine_2,
&event,
TRUE,
TRUE,
TRUE
);
status = IoCallDriver(TopOfDeviceStack, Irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event,
Executive, // WaitReason
KernelMode, // must be Kernelmode to prevent the stack getting paged out
FALSE,
NULL // indefinite wait
);
status = Irp->IoStatus.Status;
}
// <---- Do your own work here.
//
// Because you stopped the completion of the IRP in the CompletionRoutine
// by returning STATUS_MORE_PROCESSING_REQUIRED, you must call
// IoCompleteRequest here.
//
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
}
NTSTATUS
CompletionRoutine_2(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
if (Irp->PendingReturned == TRUE) {
//
// You will set the event only if the lower driver has returned
// STATUS_PENDING earlier. This optimization removes the need to
// call KeSetEvent unnecessarily and improves performance because the
// system does not have to acquire an internal lock.
//
KeSetEvent ((PKEVENT) Context, IO_NO_INCREMENT, FALSE);
}
// This is the only status you can return.
return STATUS_MORE_PROCESSING_REQUIRED;
}
สถานการณ์ที่ 3: การส่งต่อกับขั้นตอนการดำเนินการเสร็จสมบูรณ์
ในกรณีนี้ โปรแกรมควบคุมการตั้งค่าขั้นตอนการดำเนินการเสร็จสมบูรณ์ ส่งต่อการ IRP ลง แล้ว ส่งกลับค่าสถานะของโปรแกรมควบคุมที่ต่ำกว่าที่ วัตถุประสงค์ของการตั้งค่าขั้นตอนการดำเนินการเสร็จสมบูรณ์มีการ ปรับเปลี่ยนเนื้อหาของ IRP ในวิธีการกลับ
NTSTATUS
DispathRoutine_3(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
NTSTATUS status;
//
// Because you are setting completion routine, you must copy the
// current stack location to the next. You cannot skip a location
// here.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
CompletionRoutine_31,// or CompletionRoutine_32
NULL,
TRUE,
TRUE,
TRUE
);
return IoCallDriver(TopOfDeviceStack, Irp);
} ถ้าคุณกลับสถานะของโปรแกรมควบคุมที่ต่ำกว่าจากขั้นตอนการสรุปข้อมูลของคุณ:
- คุณไม่ต้องเปลี่ยนสถานะของ IRP ในขั้นตอนการดำเนินการเสร็จสมบูรณ์ นี่คือเพื่อให้แน่ใจว่า ค่าสถานะที่กำหนดใน IoStatus ของ IRP บล็อก (Irp-> IoStatus.Status) จะเหมือนกับการส่งคืนสถานะของโปรแกรมควบคุมที่ต่ำกว่า
- คุณต้องเผยแพร่ IRP สถานะรอค้างอยู่ตามที่ระบุ โดย Irp-> PendingReturned
- คุณไม่ต้องเปลี่ยน synchronicity ของ IRP
ด้วยเหตุ มีเพียงรุ่น 2 ที่ถูกต้องของขั้นตอนการดำเนินการเสร็จสมบูรณ์ในสถานการณ์นี้ (31 และ 32) ดังนี้:
NTSTATUS
CompletionRoutine_31 (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
//
// Because the dispatch routine is returning the status of lower driver
// as is, you must do the following:
//
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
return STATUS_CONTINUE_COMPLETION ; // Make sure of same synchronicity
}
NTSTATUS
CompletionRoutine_32 (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
//
// Because the dispatch routine is returning the status of lower driver
// as is, you must do the following:
//
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
//
// To make sure of the same synchronicity, complete the IRP here.
// You cannot complete the IRP later in another thread because the
// the dispatch routine is returning the status returned by the lower
// driver as is.
//
IoCompleteRequest( Irp, IO_NO_INCREMENT);
//
// Although this is an unusual completion routine that you rarely see,
// it is discussed here to address all possible ways to handle IRPs.
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
สถานการณ์ที่ 4: คิวสำหรับภายหลัง หรือส่งต่อ และนำมาใช้ใหม่
ใช้ snippet รหัสต่อไปนี้ในกรณีที่ที่โปรแกรมควบคุมที่ต้องการอย่างใดอย่างหนึ่งคิว IRP ข้อ และประมวลผลได้ในภายหลัง หรือส่งต่อ IRP โปรแกรมควบคุมที่ต่ำกว่า และนำมาใช้สำหรับหมายเลขเฉพาะของเวลาก่อนที่จะดำเนินการ IRP ขั้นตอนการส่ง IRP ค้างอยู่รอการทำเครื่องหมาย และส่งกลับค่า STATUS_PENDING เนื่องจากกำลัง IRP ที่ต้องทำให้สมบูรณ์ในภายหลังในเธรดอื่น ที่นี่ ขั้นตอนการดำเนินการเสร็จสมบูรณ์
สามารถเปลี่ยนสถานะของการ IRP ถ้าจำเป็น (contrast เพื่อใช้ในสถานการณ์สมมติก่อนหน้านี้)
NTSTATUS
DispathRoutine_4(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
NTSTATUS status;
//
// You mark the IRP pending if you are intending to queue the IRP
// and process it later. If you are intending to forward the IRP
// directly, use one of the methods discussed earlier in this article.
//
IoMarkIrpPending( Irp );
//
// For demonstration purposes: this IRP is forwarded to the lower driver.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
CompletionRoutine_41, // or CompletionRoutine_42
NULL,
TRUE,
TRUE,
TRUE
);
IoCallDriver(TopOfDeviceStack, Irp);
//
// Because you marked the IRP pending, you must return pending,
// regardless of the status of returned by IoCallDriver.
//
return STATUS_PENDING ;
}ขั้นตอนการดำเนินการเสร็จสมบูรณ์สามารถกลับ STATUS_CONTINUE_COMPLETION หรือ STATUS_MORE_PROCESSING_REQUIRED อย่างใดอย่างหนึ่ง คุณกลับ STATUS_MORE_PROCESSING_REQUIRED เฉพาะในกรณีที่คุณต้องการนำมาใช้ IRP จากเธรดอื่น และทำให้เสร็จสมบูรณ์ได้ในภายหลัง
NTSTATUS
CompletionRoutine_41(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
//
// By returning STATUS_CONTINUE_COMPLETION , you are relinquishing the
// ownership of the IRP. You cannot touch the IRP after this.
//
return STATUS_CONTINUE_COMPLETION ;
}
NTSTATUS
CompletionRoutine_42 (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
//
// Because you are stopping the completion of the IRP by returning the
// following status, you must complete the IRP later.
//
return STATUS_MORE_PROCESSING_REQUIRED ;
}
สถานการณ์ที่ 5: สมบูรณ์ IRP ในขั้นตอนการส่ง
สถานการณ์นี้แสดงวิธีการที่ดำเนินการ IRP ในขั้นตอนการส่ง
สิ่งสำคัญเมื่อคุณดำเนินการ IRP ในขั้นตอนการส่ง สถานะของชุดคำสั่งส่งคืนควรตรงกับสถานะของค่าที่ถูกกำหนดในบล็อก IoStatus ของ IRP (Irp-> IoStatus.Status)
NTSTATUS
DispatchRoutine_5(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
//
// <-- Process the IRP here.
//
Irp->IoStatus.Status = STATUS_XXX;
Irp->IoStatus.Information = YYY;
IoCompletRequest(Irp, IO_NO_INCREMENT);
return STATUS_XXX;
} ส่วนที่ 2 ของชื่อเรื่องนี้อยู่ในบทความต่อไปนี้ของ Knowledge Base:
326315
(http://support.microsoft.com/kb/326315/
)
วิธีการต่าง ๆ ของการจัดการ IRPs - แผ่นงาน cheat (ส่วนที่ 2 จาก 2)
- Walter Oneyการเขียนโปรแกรมรุ่นโปรแกรมควบคุมของ Windowsสอง Edition บทที่ 5