|
|
Kernel
Mode Driver Tutorial: Part I: “The
Skeleton KMD” by Clandestiny
|
I. Introduction
Greetings!
Here is the first of hopefully several KMD (kernel mode driver)
tutorials. KMD’s are the accepted driver
format for the Windows NT/2000/XP operating systems, but by and large they are
still a mystery. There are hundreds of
obscure structures and obtuse functions documented in the mess that calls
itself the MSDN. There are a handful of
books on the subject which are useful references if we ignore the fact that
most of them exceed 500 pages and require a PhD in computer science to get past
the first couple pages of the “introduction”.
There are even a few snippets of example KMD code lying around the
internet if you know where to look. Of
course, they too assume an advanced
background and are hardly suited to serve as examples to the programmer taking
his/her first baby steps into kernel mode programming. When I first became interested in writing a
driver, I was using Windows 98 and I started by learning about VxD’s from
Iczelion’s fantastic win32 asm series tutorials. Eventually, when I migrated to Windows 2000,
I was forced to abandon my VxD knowledge and embark on a new search for the KMD
equivalent to Iczelion’s famous tutorials.
I’m sorry to report that I never found it. After much searching, I cobbled up enough
information to understand and create a successful skeleton KMD. What you are
about to read is the result of that effort.
It is my hope that this tutorial serves as a simple introduction to
those programmers, like me, who are taking their first baby steps into KMD
development. I am by no means an expert
on the subject and readily welcome corrections or suggestions for improvement
from those more knowlegable in the field. You can contact me at clandestiny(at)despammed.com. If this tutorial generates some positive
feedback, ultimately I hope to write KMD tutorials on other low-level
programming tasks like interrupt hooking and memory access / management.
IIa. Getting Started
For the skeleton KMD you will need at least the
following include files / libraries... I used the include files / libraries
kindly provided by Four-F in his KMD kit.
§
ntoskrnl.lib
§
ntddk.inc
§
ntoskrnl.inc
§
ntstatus.inc
§
Strings.mac – not
strictly required but very helpful nonetheless
NOTE: The code presented in this tutorial was written and
assembled using MASM version 7.
I would
also like to add that DriverStudio by Numega contains a very handy utility
called DriverMonitor that can be used to start, stop, load, and unload drivers
while testing and debugging. I recommend
obtaining it both for DriverMonitor and for SoftICE (IMO, the BEST ring 0
debugger). There is also some KMD example code included with DriverStudio if
you know C++.
IIb. Driver Structure
Unlike the VxDs of Win9x which followed the LE
(linear executable) format, KMDs structurally follow the standard PE (portable
executable format) of .exe’s and .dll’s and compile to a .sys file
extension.
On the programmatic level, the skeleton kernel mode
driver can be broken down into 3 basic functions, DriverEntry,
DriverDispatcher, and DriverUnload. The
purpose of Driver Entry is to perform initialization tasks. Unless your driver is intended to be loaded
upon bootup, it’s driver entry function will be called
from within a ring 3 loader that you write.
The second primary function of your KMD will be the DriverDispatcher
routine. As its name implies, the
DriverDispatcher routine handles messages dispatched to the driver by either
the OS or another ring 3 application via the DeviceIoControl interface. As such, it can act as a communication
conduit between ring 0 and ring 3 where the ring 0 driver provides privaleged
“services” to a ring 3 program for tasks it would be unable to accomplish under
ring 3 restrictions (ie. like accessing the IDT,GDT, and LDT system descriptors
or low level hooking and spying on I/O functions). Used in this manner, a KMD functions much
like a ring 0 dll. Finally, like the DriverEntry function, DriverUnload will be
called from a ring 3 application. It
performs standard deinitialization and cleanup (freeing memory, closing
handles, removing hooks, ect).
Ø
The Driver
Entry Routine
This is the one function common to all KMD drivers
and as its name implies, serves as the driver entry point. The first function
of the DriverEntry is to create a "device object" using the IoCreateDevice call. Assuming this is successful, it will secondarily
set up a "symbolic link" using the IoCreateSymbolicLink function. The symbolic link is optional for the
functioning of the driver, BUT is required if you need a ring 3 application to
be able to communicate with your driver.
Indeed, the symbolic link basically creates a name for your driver which
will be used to access it in ring 3 using the CreateFile call. The CreateFile call takes this name as its
path parameter. Lastly, DriverEntry must
set up pointers to functions for any requests that it will handle (these
requests are of the form IRP_MJ_XXX).
The minimum standard requests that must be handled for the driver to
work are IRP_MJ_CREATE, IRP_MJ_CLOSE,
AND IRP_MJ_CLEANUP. The IRP_MJ_DEVICE_CONTROL request must
be handled if the driver is to be able to communicate with ring 3 apps via the DeviceIoControl interface. The driver object
maintains an array of pointers to the dispatch procedures that will be called
when these requests are recieved. It is
the responsiblity of DriverEntry to set the function pointers in this array. A successful DriverEntry function returns the
value STATUS_SUCCESS (0X00). I should
also mention that KMDs typically only use strings in the UNICODE format. Four-f
provides some convienient macros for dealing with them in Strings.mac found in
his KMD Kit.
We can summarize the DriverEntry routine’s
responsiblites as follows…
1. Create the device using IoCreateDevice
2. Set up a SymbolicLink using IoCreateSymbolicLink if your driver needs to be able to
communicate with ring 3 applications
3. Set up the entry points for the
IRP_MJ requests your driver services
4. Set the DriverUnload entry point
|
DriverEntry PROC pDriverObject:PDRIVER_OBJECT,
pRegistryPath:PUNICODE_STRING LOCAL status:DWORD pushad mov esi, pDriverObject ASSUME esi:PTR DRIVER_OBJECT mov edi, OFFSET
gDeviceObject ASSUME edi:PTR DEVICE_OBJECT ;------------------------------------ ;Create the device &
Set up the symbolic ;link so ring 3 apps can
communicate w/ ;our ring 0 driver ;------------------------------------ invoke IoCreateDevice,esi,0,$CCOUNTED_UNICODE_STRING("\\Device\\skeleton",
uDeviceName, 4),FILE_DEVICE_UNKNOWN,0,FALSE,edi .IF eax ==
STATUS_SUCCESS invoke IoCreateSymbolicLink,$CCOUNTED_UNICODE_STRING("\\DosDevices\\skeleton",
uSymbolicLinkName, 4),OFFSET uDeviceName .IF eax == STATUS_SUCCESS ;------------------------------------ ;Set up dispatch routine entry points ;for IRP_MJ_Xxx requests ;------------------------------------ mov [esi].MajorFunction[IRP_MJ_DEVICE_CONTROL*(sizeof
PVOID)],OFFSET DriverDispatcher mov [esi].MajorFunction[IRP_MJ_CREATE*(sizeof
PVOID)],OFFSET DriverDispatcher mov [esi].MajorFunction[IRP_MJ_CLOSE*(sizeof
PVOID)],OFFSET DriverDispatcher mov [esi].MajorFunction[IRP_MJ_CLEANUP*(sizeof
PVOID)],OFFSET DriverDispatcher ;---------------------------------- ;Set DriverUnload entry point ;---------------------------------- mov [esi].DriverUnload, OFFSET DriverUnload .ENDIF .ENDIF mov status,eax popad mov eax,status ret DriverEntry endp |
Ø
The Driver
Dispatcher Routine
The DriverDispatcher routine functions more or less
as the WndProc does to a standard win32 application. It processes messages / requests. A request is defined by an IRP also known I/O
request packet. Every driver is assigned a unique location in the IRP stack
which contains information about its requests.
When the driver recieves a request, it looks up the request in the IRP stack using IoGetCurrentStackLocation. These requests can fall into 3 categories:
1. Standard
Requests necessary for the driver to function like IRP_MJ_CREATE, IRP_MJ_CLOSE, and IRP_MJ_CLEANUP. The programmer's responsiblity at a bare
minimum is to return STATUS_SUCCESS in response to these requests.
2. Programmer
Defined Requests which fall under IRP_MJ_DEVICE_CONTROL
are those custom services provided to ring 3 applications via the DeviceIoControl interface. As in the VxD, you
will look up the IOCTL code and branch off to the procedure that needs to be
handled. In order for the DeviceIoControl interface
to work with a KMD, it is necessary to write an IOCTL header file. The IOCTL header file defines the device
type, service control code value, buffer transfer type, and access type for
both the driver and the requestor of its services. All together these parameters define an IOCTL
value. IOCTL values are obtained by
calling the macro CTL_CODE in your IOCTL header file. The header file will be included both in the
driver build and the ring 3 application which desires to communicate with the
driver. The IOCTL parameters are as follows:
CTL_CODE( DeviceType,
ControlCode, TransferType, RequiredAccess)
|
Parameter Description |
|
|
DeviceType |
File_Device_XXX values supplied to IoCreateDevice §
00h to 7FFh – reserved for Microsoft §
8000h to FFFh – customer defined |
|
Control
Code |
Driver defined IOCTL code §
000h to 7FFh – reserved for Microsoft §
800h to FFFh – customer defined |
|
Transfer Type |
Buffer passing mechanism for this control code §
METHOD_INDIRECT §
METHOD_BUFFERED §
METHOD_OUT_DIRECT §
METHOD_NEITHER |
|
Required
Access |
Requestor access requirement §
FILE_ANY_ACCESS §
FILE_READ_DATA §
FILE_WRITE_DATA §
FILE_READ_DATA | FILE_WRITE_DATA |
In the skeleton driver I have defined 1 IOCTL value
to serve as an example for how the DeviceIoControl Interface operates. I declared it in ctrl_codes.inc using the CTL_CODE
macro as follows:
SERVICE_SAY_HELLO equ CTL_CODE (FILE_DEVICE_UNKNOWN,801H,METHOD_NEITHER,FILE_ANY_ACCESS)
For the device type I delcared ,
FILE_DEVICE_UNKNOWN, the same device type as I declared when I created it using
IoCreateDevice. I set the control code at 801, but it could have been any
number within the customer defined range of 800h to FFFh. The important point when choosing control
codes is that all of the control codes for your device are unique values within
that range. I set the transfer type as METHOD_NEITHER for this service. Basically this means that the I/O manager
directly passes you the address to the input or output buffer sent by the
DeviceIoControl call. In other words,
there is no error checking done on this address by the I/O manager and
consequently, it is the least safe method of passing your data to and from the
driver. The METHOD_BUFFERED,
METHOD_INDIRECT, and METHOD_OUT_DIRECT all have checks performed on them by the
I/O Manager and are therefore safer to use.
Passing data via the DeviceIoControl interface
differs depending on the transfer type you’ve specified…
For METHOD_BUFFERED, the I/O manager allocates a
single buffer which is accessed through pIRP.AssoicatedIrp.SystemBuffer. Because this buffer is used for both input
and output, the driver must be careful to extract all input information before
writing output back to the buffer.
For METHOD_IN_DIRECT, the I/O manager checks the
input buffer and builds an MDL for it.
It then stores a pointer to the MDL in pIRP.MdlAddress. The output
buffer is accessed through pIRP.AssociatedIrp.SystemBuffer.
For METHOD_OUT_DIRECT, the I/O manager checks the
output buffer and builds an MDL for it.
It then stores a pointer to the MDL in pIRP.MdlAddress. The input buffer
is accessed through pIRP.AssociatedIrp.SystemBuffer.
For METHOD_NEITHER, the addresses specified by the
user in the DeviceIoControl call are provided directly to the driver with no
error checking or verification. The input buffer can be accessed though
pIRP.Parameters.DeviceIoControl.Type3InputBuffer and the output buffer can be
accessed though pIrp.UserBuffer.
As I mentioned previously, I handled 1 programmer
defined request in the skeleton driver. It’s function was simply return a string from ring 0 to ring
3 which is printed out by the ring 3 applciation in a messagebox. When handling a user defined request, the
first step is to verify that it is an IRP_MJ_DEVICE_CONTROL request. If it is, then you will go on to check the
CTRL code to determine what service is being requested. There could be several which you would have
to check, but in this case the only request is defined as SERVICE_SAY_HELLO.
The function of SERVICE_SAY_HELLO is to copy a string into the output buffer
and return STATUS_SUCCESS. Remember, I
am using METHOD_NEITHER for the data transfer so the data will be copied into
the location addressed by pIrp.UserBuffer.
.ELSEIF [eax].MajorFunction == IRP_MJ_DEVICE_CONTROL ;check for
DeviceIoControl request from user
.IF
[eax].Parameters.DeviceIoControl.IoControlCode == SERVICE_SAY_HELLO ;determine what
service is being requested
invoke CopyString,[edi].UserBuffer,OFFSET
msgHello,[eax].Parameters.DeviceIoControl.OutputBufferLength ;copy string into
output buffer
mov[edi].IoStatus.Status,STATUS_SUCCESS
.ELSE
mov[edi].IoStatus.Status,STATUS_NOT_IMPLEMENTED
.ENDIF
3. Undefined
Requests, or requests which are not handled directly by the given driver.
For these, it is the programmers responsiblity to return the STATUS_NOT_IMPLEMENTED value.
|
DriverDispatcher PROC pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP LOCAL status:DWORD pushad mov esi, pDeviceObject ASSUME esi: PTR DEVICE_OBJECT mov edi, pIrp ASSUME edi:PTR _IRP ;---------------------------------- ;Determine what driver service is ;being requested ;---------------------------------- IoGetCurrentIrpStackLocation
edi assume eax:PTR IO_STACK_LOCATION ;--------------------------------- ;Standard Requests ;--------------------------------- .IF [eax].MajorFunction == IRP_MJ_CREATE ||
[eax].MajorFunction == IRP_MJ_CLOSE
|| [eax].MajorFunction == IRP_MJ_CLEANUP mov
[edi].IoStatus.Status, STATUS_SUCCESS ;-------------------------------- ;Programmer Defined Requests ;-------------------------------- .ELSEIF [eax].MajorFunction == IRP_MJ_DEVICE_CONTROL .IF
[eax].Parameters.DeviceIoControl.IoControlCode == SERVICE_SAY_HELLO invoke
CopyString,[edi].UserBuffer,OFFSET
msgHello,[eax].Parameters.DeviceIoControl.OutputBufferLength mov[edi].IoStatus.Status,STATUS_SUCCESS .ELSE mov[edi].IoStatus.Status,STATUS_NOT_IMPLEMENTED .ENDIF ;-------------------------------- ;Undefined Requests ;-------------------------------- .ELSE mov[edi].IoStatus.Status,STATUS_NOT_IMPLEMENTED .ENDIF push [edi].IoStatus.Status pop status mov[edi].IoStatus.Information,0 invoke IoCompleteRequest,edi,IO_NO_INCREMENT popad mov eax,status ret DriverDispatcher endp |
Ø
The Driver Unload
Routine
The DriverUnload routine is pretty self
explanatory. It will be called when the
loader invokes the ControlService API with a
SERVICE_CONTROL_STOP message. DriverUnload basically unwinds the actions taken by
the DriverEntry function. It will
perform any driver specific cleanup and is at a minimum required to delete the
device object (IoDeleteDevice) and the
symbolic link (IoDeleteSymbolicLink). Failure to perform either of these actions
will corrupt the internal service database entry for the driver preventing it
from being loaded again (until you reboot L).
|
DriverUnload PROC
pDriverObject:PDRIVER_OBJECT pushad mov esi, pDriverObject ASSUME esi: PTR DRIVER_OBJECT invoke IoDeleteSymbolicLink, OFFSET uSymbolicLinkName invoke IoDeleteDevice,[esi].DeviceObject popad ret DriverUnload endp |
IIc. The Loader
If you have Driver Studio, you can use the handy little
utitlity “Driver Monitor” to load, start, stop, and unload drivers for testing
purposes. However, if your driver must provide services to a ring 3 application will, the ring 3 application will will manually
have to install the driver as a service in order to be able to communicate with
it. The procedure for loading a driver
is as follows:
1. Obtain a handle to the service manager using OpenSCManager.
2. Define the driver using CreateService. This creates an entry in the
service database for the driver. Note, that this information is kept in the registry
under...HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\"My Driver's
Name". Realize, that CreateService
does not *load* the driver. It simply
creates an entry in the service database so that it can be recognized.
3. Open the driver using OpenService. This call loads the driver, but realize that
this call does not *start* the driver.
4. Finally start the driver using the StartService function. Note that this function is what finally calls
the DriverEntry routine. If there are
errors in your DriverEntry routine, expect StartService to fail.
5. Now you can open a handle to the driver in a
ring 3 application using CreateFile and send
requests via the DeviceIoControl.
6. Stop the driver by sending a
SERVICE_CONTROL_STOP via ControlService. Note that this function calls the
DriverUnload routine. If there are
errors in your DriverUnload routine, expect this function to fail and expect to
be unable to load the driver again unless you reboot. This is because if this call fails, or the
driver crashes before executing this call, the service database entry for the
driver will be corrupted until you reboot.
7. Finally delete the service using DeleteService and release the handle to the
service control database using CloseServiceHandle. DeleteService is the function responsible for
deleting the actual entry in the service database and the corresponding key in
the registry.
|
;THE LOADER .code start: TestDriver PROC ;-------------------------------------------------- ;Obtain a
handle to the service control manager ;-------------------------------------------------- invoke OpenSCManager,
0, 0, SC_MANAGER_ALL_ACCESS mov
hScManager,eax ;------------------------------------------------- ;Add the
service to the system ;------------------------------------------------- invoke
GetFullPathName,ADDR DriverName,256,ADDR FilePath,ADDR pFilePart ;------------------------------------------------ ;Register
the service w/ the system ;------------------------------------------------ invoke CreateService,hScManager,ADDR ServiceName,ADDR
ServiceName,SERVICE_ALL_ACCESS,SERVICE_KERNEL_DRIVER, \ SERVICE_DEMAND_START,SERVICE_ERROR_NORMAL,ADDR
FilePath,0,0,0,0,0 xchg eax,ebx invoke
GetLastError .IF (ebx !=
0) || (eax == ERROR_SERVICE_EXISTS) ;service database entry already exists
for driver mov
hService,eax ;--------------------------------------------------------------- ;Load
the service /driver ;--------------------------------------------------------------- invoke
OpenService,hScManager,ADDR
ServiceName,SERVICE_ALL_ACCESS .IF
eax != 0 mov
hService,eax ;-------------------------------------------------- ;Start
the service to set the service to the running ;state. StartService will call the DriverEntry procedure ;-------------------------------------------------- invoke
StartService,hService,0,0 .IF
eax != 0 ;--------------------------------------------------- ;Obtain
a handle to the loaded driver for DeviceIoControl ;interface
communcation ;--------------------------------------------------- invoke
CreateFile,ADDR DriverPath ,GENERIC_READ or
GENERIC_WRITE,0,0,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0 .IF
eax != INVALID_HANDLE_VALUE mov hDriver,
eax .ELSE invoke
MessageBoxA,NULL,ADDR lpMsgFileErrorText,ADDR lpMsgErrorTitle,MB_OK jmp file_error ;CreateFile error .ENDIF ;-------------------------------------------------- ;Send
service request SERVICE_SAY_HELLO ;-------------------------------------------------- invoke
DeviceIoControl,hDriver,SERVICE_SAY_HELLO,0,0,ADDR
lpOutBuffer,256,ADDR BytesReturned,0 mov
edi,dword ptr lpOutBuffer .IF
edi == 0 invoke MessageBoxA,NULL,ADDR lpMsgTransferErrorText,ADDR
lpMsgErrorTitle,MB_OK ;transfer error jmp
stop_service .ENDIF invoke MessageBoxA,NULL,ADDR lpOutBuffer,ADDR
lpMsgTitle,MB_OK stop_service: invoke CloseHandle,hDriver ;Close the handle file_error: invoke
ControlService,hService,SERVICE_CONTROL_STOP,ADDR
ServiceStatus invoke
DeleteService,hService jmp
exit .ELSE jmp
stop_service ;StartService error .ENDIF .ELSE jmp
error ;OpenService error .ENDIF invoke
CloseServiceHandle,hScManager .ELSE jmp
error ;CreateService error .ENDIF ;-------------------------------------------------- ;Close the
service to release manager and service ;handles ;-------------------------------------------------- error: invoke
CloseServiceHandle,hScManager invoke MessageBoxA,NULL,ADDR lpMsgErrorText,ADDR
lpMsgErrorTitle,MB_OK exit: ret TestDriver endp end start |
III. Credits
A special
credit and thanks to Four-F for making
his handy KMD kit available to MASM programmers and to RAMA for his example KMD which aided me in figuring out how to
write a kernel mode driver.
Last but
not least, a personal thanks to my friend Kayaker
for his past, present, and future help and encouragement J.
Additional
references include the MSDN and Undocumented Windows Secrets: A
Programmer’s cookbook (I got the undocumented secrets book from
amazon.com for $10 used and would highly recommend it to anyone interested in
ring 0 programming).
IV. Appendix
NOTE:
Yes, I shamelessly copied these function & structure definitions from the
MSDN documentation ;-) I figured it
would be easier for you to follow if I conveniently referenced them here rather
than forcing you to search for them on the MSDN site J
Each driver object represents the image of a loaded
kernel-mode driver. A pointer to the driver object is an input parameter to a
driver’s DriverEntry, AddDevice, and
optional Reinitialize
routines and to its Unload routine, if
any.
A driver object is partially opaque. Driver writers must
know about certain members of a driver object to initialize a driver and to
unload it if the driver is unloadable. The following members of the driver
object are accessible to drivers.
Accessible Members
PDEVICE_OBJECT DeviceObject
Pointer to the device
objects created by the driver. This member is automatically updated when the
driver calls IoCreateDevice
successfully. A driver can use this member and the NextDevice member of
DEVICE_OBJECT to step through a list of all the device objects that the driver
created.
PDRIVER_EXTENSION DriverExtension
Pointer to the driver
extension. The only accessible member of the driver extension is DriverExtension->AddDevice,
into which a driver's DriverEntry routine stores the driver's AddDevice routine.
PUNICODE_STRING HardwareDatabase
Pointer to the \Registry\Machine\Hardware
path to the hardware configuration information in the registry.
PFAST_IO_DISPATCH FastIoDispatch
Pointer to a structure
defining the driver’s fast I/O entry points. This member is used
only by FSDs and network transport drivers.
PDRIVER_INITIALIZE DriverInit
The entry point for the
DriverEntry
routine, which is set up by the I/O Manager.
PDRIVER_STARTIO DriverStartIo
The entry point for the driver’s StartIo routine,
if any, which is set by the DriverEntry routine when the driver
initializes. If a driver has no StartIo routine, this member is NULL.
PDRIVER_UNLOAD DriverUnload
The entry point for the driver’s Unload routine, if
any, which is set by the DriverEntry routine when the driver
initializes. If a driver has no Unload routine, this member is NULL.
PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION+1]
A dispatch table consisting of an array of
entry points for the driver’s DispatchXxx routines. The array's index
values are the IRP_MJ_Xxx values representing each IRP major function code.
Each driver must set entry points in this array for the IRP_MJ_Xxx
requests that the driver handles. For more information, see Writing Dispatch Routines.
Each DispatchXxx routine is declared as follows:
NTSTATUS
(*PDRIVER_DISPATCH) (
IN PDEVICE_OBJECT
DeviceObject,
IN PIRP Irp
);
Headers
Defined in wdm.h and ntddk.h. Include wdm.h
or ntddk.h.
Comments
Each kernel-mode driver’s initialization routine should be
named DriverEntry so the
system will load the driver automatically. If this routine’s name is something
else, the driver writer must define the name of the initialization routine for
the linker; otherwise, the OS loader or I/O Manager cannot find the driver’s
transfer address. The names of other standard driver routines can be chosen at
the discretion of the driver writer.
A driver must set its DispatchXxx entry points in
the driver object that is passed in to the DriverEntry routine when the
driver is loaded. A device driver must set one or more DispatchXxx entry
points for the IRP_MJ_XXX that any driver of the same type of device is
required to handle. A higher-level driver must set one or more DispatchXxx
entry points for all the IRP_MJ_XXX that it must pass on to the
underlying device driver. Otherwise, a driver is not sent IRPs for any IRP_MJ_XXX
for which it does not set up a DispatchXxx routine in the driver object.
For more information about the set of IRP_MJ_XXX that drivers for
different types of underlying devices are required to handle, see IRP Major Function Codes.
The DriverEntry routine also sets the driver’s AddDevice,
StartIo and/or Unload entry points, if any, in the driver object.
The HardwareDatabase string can be used by device
drivers to get hardware configuration information from the registry when the
driver is loaded. A driver is given read-only access to this string.
The RegistryPath input to the DriverEntry
routine points to the \Registry\Machine\System\CurrentControlSet\Services\DriverName
key, where the value entry of DriverName identifies the driver. As for
the HardwareDatabase in the input driver object, a driver is given
read-only access to this string.
Undocumented members within a driver object should be
considered inaccessible. Drivers with dependencies on object member locations
or on access to undocumented members might not remain portable and
interoperable with other drivers over time.
See Also
DriverEntry,
IoCreateDevice, IoDeleteDevice, StartIo, Unload
A device object represents a logical, virtual, or physical
device for which a driver handles I/O requests.
Accessible Members
PDRIVER_OBJECT DriverObject
Pointer to the driver object, representing the
driver’s loaded image, that was input to the DriverEntry
and AddDevice routines.
PDEVICE_OBJECT NextDevice
Pointer to the next
device object, if any, created by the same driver. The I/O Manager
updates this list at each successful call to IoCreateDevice or IoCreateDeviceSecure.
A driver that is being unloaded must walk the list of its device objects and
delete them. A driver that recreates its device objects dynamically also uses
this field.
PIRP CurrentIrp
Pointer to the current
IRP if the driver has a StartIo routine
whose entry point was set in the driver object and if the driver is currently
processing IRP(s). Otherwise, this field is NULL.
ULONG Flags
Device drivers OR this field in their newly
created device objects with one or more of the following system-defined values:
DO_BUFFERED_IO or DO_DIRECT_IO
Higher-level drivers OR
the field with the same value as the next-lower driver, except possibly for
highest-level drivers.
DO_BUS_ENUMERATED_DEVICE
The system sets this flag in each PDO. Drivers
must not modify this flag.
DO_DEVICE_INITIALIZING
The I/O Manager sets this flag when it creates
the device object.
A device function or filter driver clears the flag in its AddDevice routine,
after attaching the device object to the device stack, establishing the device
power state, and ORing the field with one of the power flags (if necessary).
The PnP Manager checks that the flag is clear after return from AddDevice.
DO_POWER_INRUSH
Drivers of devices that require inrush current
when powering on must set this flag. A driver cannot set both this flag and
DO_POWER_PAGABLE.
DO_POWER_PAGABLE
Windows® 2000 and later drivers that are
pageable, are not part of the paging path, and do not require inrush current
must set this flag. The system calls such drivers at IRQL PASSIVE_LEVEL.
Drivers cannot set both this flag and DO_POWER_INRUSH.
All WDM, Windows 98, and Windows Me drivers must set
DO_POWER_PAGABLE.
DO_VERIFY_VOLUME
Removable-media drivers set this flag while
processing transfer requests. Such drivers should also check for this flag in
the target for a transfer request before transferring any data. See the Supporting Removable Media
for details.
For more information about setting the Flags field,
see Initializing a Device Object.
ULONG Characteristics
Specifies one or more
system-defined constants, ORed together, that provide additional information
about the driver's device. The constants include:
FILE_AUTOGENERATED_DEVICE_NAME
Directs the I/O Manager
to generate a name for the device, instead of the caller specifying a DeviceName
when calling this routine. The I/O Manager ensures that the name is
unique. This characteristic is typically specified by a PnP bus driver to
generate a name for a physical device object (PDO) for a child device on its
bus. This characteristic is new for Windows 2000 and Windows 98.
FILE_CHARACTERISTIC_PNP_DEVICE
Indicates that the device
object is part of a Plug and Play stack. This flag is required if a bus driver
(or bus filter driver) registers WMI support for a device object that has not
yet received the IRP_MN_START_DEVICE request. It is also required if a function
or filter driver registers for WMI before attaching to its device stack.
FILE_DEVICE_IS_MOUNTED
Indicates that a file
system is mounted on the device. Drivers should not set this characteristic.
FILE_DEVICE_SECURE_OPEN
(Windows NT® 4.0 SP5 and later)
Directs the I/O Manager to apply the
security descriptor of the device object to relative opens and trailing file
name opens on the device. For more information, see Controlling Device Namespace
Access.
FILE_FLOPPY_DISKETTE
Indicates that the
device is a floppy disk device.
FILE_READ_ONLY_DEVICE
Indicates that the
device cannot be written to.
FILE_REMOTE_DEVICE
Indicates that the
device is remote.
FILE_REMOVABLE_MEDIA
Indicates that the
storage device supports removable media.
Note that this characteristic indicates removable media,
not a removable device. For example, drivers for JAZ drive devices
should specify this characteristic, but drivers for PCMCIA flash disks should
not.
FILE_VIRTUAL_VOLUME
Indicates that the
volume is virtual. Drivers should not set this characteristic.
FILE_WRITE_ONCE_MEDIA
Indicates that the
device supports write-once media.
Drivers do not set this member directly. For more information
about setting device characteristics, see Specifying Device Characteristics.
PVOID DeviceExtension
Pointer to the device
extension. The structure and contents of the device extension are
driver-defined. The size is driver-determined, specified in the driver’s call
to IoCreateDevice or IoCreateDeviceSecure.
For more information about device extensions, see Device Extensions.
DEVICE_TYPE DeviceType
Set by IoCreateDevice,
using the value specified for that routine's DeviceType parameter. For
more information, see Specifying Device Types.
CCHAR StackSize
Specifies the minimum number of stack
locations in IRPs to be sent to this driver. IoCreateDevice and
IoCreateDeviceSecure
sets this field to one in newly created device
objects; lowest-level drivers can therefore ignore this field. The I/O manager
automatically sets the StackSize field in a higher-level driver’s device
object to the appropriate value if the driver calls IoAttachDevice or IoAttachDeviceToDeviceStack.
Only a higher-level driver that chains itself over another driver with IoGetDeviceObjectPointer
must explicitly set the value of StackSize in its own device object(s)
to (1 + the StackSize value of the next-lower driver’s device object).
ULONG AlignmentRequirement
Specifies the device's
address alignment requirement for data transfers. The value must be one
of the FILE_XXX_ALIGNMENT values defined in wdm.h and ntddk.h.
For more information, see Initializing a Device Object.
Also see GetDmaAlignment
and ZwQueryInformationFile.
Headers
Defined in wdm.h and ntddk.h. Include wdm.h
or ntddk.h.
Comments
The operating system represents devices by device objects.
For more information, see Device Objects and Device Stacks.
Drivers create device objects by using the IoCreateDevice and
IoCreateDeviceSecure
routines. For more information on creating device objects, see Creating a Device Object.
A device object is partially opaque. Drivers do not set
members of the device object directly, unless otherwise documented. For
information on the members that drivers can modify directly, see Initializing a Device Object.
For other device object properties, see Properties of Device Objects.
Undocumented members within a device object must be
considered inaccessible. Drivers with dependencies on object member locations
or access to undocumented members might not remain portable and interoperable
with other drivers over time.
The system-supplied video port driver sets up the fields
of the device objects it creates on behalf of video miniport drivers.
The system-supplied SCSI port driver sets up the fields of
the device objects it creates on behalf of SCSI miniport drivers.
The system-supplied NDIS library sets up the fields of the
device objects it creates on behalf of NDIS miniport drivers.
See Also
DRIVER_OBJECT, IoAttachDevice, IoAttachDeviceToDeviceStack,
IoCreateDevice, IoDeleteDevice, IoGetDeviceObjectPointer
The IoCreateDevice routine creates a device object
for use by a driver.
NTSTATUS
IoCreateDevice(
IN PDRIVER_OBJECT DriverObject,
IN ULONG DeviceExtensionSize,
IN PUNICODE_STRING DeviceName OPTIONAL,
IN DEVICE_TYPE DeviceType,
IN ULONG DeviceCharacteristics,
IN BOOLEAN Exclusive,
OUT PDEVICE_OBJECT *DeviceObject
);
Parameters
DriverObject
Pointer to the driver
object for the caller. Each driver receives a pointer to its driver object in a
parameter to its DriverEntry
routine. WDM function and filter drivers also receive a driver object pointer
in their AddDevice
routines.
DeviceExtensionSize
Specifies the
driver-determined number of bytes to be allocated for the device extension of
the device object. The internal structure of the device extension is driver-defined.
For more information about device extensions, see Device Extensions.
DeviceName
Optionally points to a buffer containing a
zero-terminated Unicode string that names the device object. The string must be
a full path name. WDM filter and function drivers do not name their device
objects. For more information, see Named Device Objects.
DeviceType
Specifies one of the
system-defined FILE_DEVICE_XXX constants that indicate the type of
device (such as FILE_DEVICE_DISK, FILE_DEVICE_KEYBOARD, etc.) or a
vendor-defined value for a new type of device. For more information,
see Specifying Device Types.
DeviceCharacteristics
Specifies one or more
system-defined constants, ORed together, that provide additional information
about the driver's device. For a list of possible device
characteristics, see DEVICE_OBJECT. For
more information on how to specify device characteristics, see Specifying Device Characteristics.
Most drivers specify FILE_DEVICE_SECURE_OPEN for this parameter.
Exclusive
Reserved for system
use.
Drivers set this parameter to FALSE.
DeviceObject
Pointer to a variable that
receives a pointer to the newly created DEVICE_OBJECT
structure. The DEVICE_OBJECT structure is allocated from nonpaged pool.
Return Value
IoCreateDevice returns STATUS_SUCCESS on success, or the
appropriate NTSTATUS error code on failure. A partial list of
the failure codes returned by this function include:
STATUS_INSUFFICIENT_RESOURCES
STATUS_OBJECT_NAME_EXISTS
STATUS_OBJECT_NAME_COLLISION
Headers
Declared in wdm.h and ntddk.h. Include wdm.h
or ntddk.h.
Comments
IoCreateDevice creates a device object and returns a pointer
to the object. The caller is responsible for deleting the object when it is no
longer needed by calling IoDeleteDevice.
IoCreateDevice can only be used to create an unnamed device
object, or a named device object for which a security descriptor is set by an
INF file. Otherwise, drivers must use IoCreateDeviceSecure
to create named device objects. For more information, see Creating a Device Object.
The caller is responsible for setting certain members of the returned device
object. For more information, see Initializing a Device Object
and the device-type-specific documentation for your device.
Be careful to specify the DeviceType and DeviceCharacteristics
values in the correct parameters. Both parameters use system-defined FILE_XXX
constants and some driver writers specify the values in the wrong parameters by
mistake.
Device objects for disks, tapes, CD-ROMs, and RAM disks
are given a Volume Parameter Block (VPB) that is initialized to indicate that
the volume has never been mounted on the device.
If a driver's call to IoCreateDevice returns an
error, the driver should release any resources that it allocated for that
device.
Callers of IoCreateDevice must be running at
IRQL < DISPATCH_LEVEL.
See Also
DEVICE_OBJECT, IoAttachDevice, IoAttachDeviceToDeviceStack,
IoCreateDeviceSecure,
IoCreateSymbolicLink,
IoDeleteDevice
The IoCreateSymbolicLink routine sets up a symbolic
link between a device object name and a user-visible
name for the device.
NTSTATUS
IoCreateSymbolicLink(
IN PUNICODE_STRING SymbolicLinkName,
IN PUNICODE_STRING DeviceName
);
Parameters
SymbolicLinkName
Pointer to a buffered
Unicode string that is the user-visible name.
DeviceName
Pointer to a buffered Unicode
string that is the name of the driver-created device object.
Return Value
IoCreateSymbolicLink returns STATUS_SUCCESS if the symbolic
link object was created.
Headers
Declared in wdm.h and ntddk.h. Include wdm.h
or ntddk.h.
Comments
WDM drivers do not name device objects and therefore
should not use this routine. Instead, a WDM driver should call IoRegisterDeviceInterface
to set up a symbolic link.
For more information on when to use IoCreateSymbolicLink,
see Named Device Objects.
Callers of IoCreateSymbolicLink must be running at
IRQL = PASSIVE_LEVEL.
See Also
IoRegisterDeviceInterface,
IoAssignArcName, IoCreateUnprotectedSymbolicLink,
IoDeleteSymbolicLink
Each driver-specific I/O
stack location (IO_STACK_LOCATION) for every IRP contains a major function code (IRP_MJ_XXX),
which tells the driver what operation it or the underlying device driver should
carry out to satisfy the I/O request. Each kernel-mode driver must provide dispatch routines for the major function codes
that it must support.
The specific operations a
driver carries out for a given IRP_MJ_XXX code depend somewhat on the
underlying device, particularly for IRP_MJ_DEVICE_CONTROL and
IRP_MJ_INTERNAL_DEVICE_CONTROL requests. For example, the requests sent to a
keyboard driver are necessarily somewhat different from those sent to a disk
driver. However, the I/O Manager defines the parameters and I/O stack location
contents for each system-defined major function code.
Every higher-level driver
must set up the appropriate I/O stack location in IRPs for the next-lower-level
driver and call IoCallDriver, either with each input IRP, or
with a driver-created IRP (if the higher-level driver holds on to the input
IRP). Consequently, every intermediate driver must supply a dispatch routine
for each major function code that the underlying device driver handles.
Otherwise, a new intermediate driver will “break the chain” whenever an
application or still higher-level driver attempts to send an I/O request down
to the underlying device driver.
File system drivers also
handle a required subset of system-defined IRP_MJ_XXX function codes,
some with subordinate IRP_MN_XXX function codes.
Drivers handle IRPs set
with some or all of the following major function codes:
IRP_MJ_INTERNAL_DEVICE_CONTROL
The input and output
parameters described in this section are the function-specific parameters in
the IRP.
For more information about
IRPs, see Handling IRPs.
The IoDeleteSymbolicLink routine removes a symbolic
link from the system.
NTSTATUS
IoDeleteSymbolicLink(
IN PUNICODE_STRING SymbolicLinkName
);
Parameters
SymbolicLinkName
Pointer to a buffered
Unicode string that is the user-visible name for the symbolic link.
Return Value
IoDeleteSymbolicLink returns STATUS_SUCCESS if the symbolic
link object is deleted.
Headers
Declared in wdm.h and ntddk.h. Include wdm.h
or ntddk.h.
Comments
Callers of IoDeleteSymbolicLink must be running at
IRQL = PASSIVE_LEVEL.
See Also
IoCreateSymbolicLink,
IoCreateUnprotectedSymbolicLink,
IoDeassignArcName
The IoDeleteDevice routine removes a device object
from the system, for example, when the underlying device is removed from the
system.
VOID
IoDeleteDevice(
IN PDEVICE_OBJECT DeviceObject
);
Parameters
DeviceObject
Pointer to the device
object to be deleted.
Return Value
None
Headers
Declared in wdm.h and ntddk.h. Include wdm.h
or ntddk.h.
Comments
When handling a PnP IRP_MN_REMOVE_DEVICE request, a PnP
driver calls IoDeleteDevice to delete any associated device objects. See
Handling an IRP_MN_REMOVE_DEVICE
Request for details.
A legacy driver should call this routine when it is being
unloaded or when its DriverEntry
routine encounters a fatal initialization error, such as being unable to
properly initialize a physical device. This routine also is called when a
driver reconfigures its devices dynamically. For example, a disk driver called
to repartition a disk would call IoDeleteDevice to tear down the device
objects representing partitions to be replaced.
A driver must release certain resources for which the
driver supplied storage in its device extension before it calls IoDeleteDevice.
For example, if the driver stores the pointer to its interrupt object(s) in the
device extension, it must call IoDisconnectInterrupt
before calling IoDeleteDevice.
A driver can call IoDeleteDevice only once for a
given device object.
When a driver calls IoDeleteDevice, the I/O Manager
deletes the target device object if there are no outstanding references to it.
However, if any outstanding references remain, the I/O Manager marks the device
object as "delete pending" and deletes the device object when the
references are released.
Callers of IoDeleteDevice must be running at
IRQL < DISPATCH_LEVEL.
See Also
IoCreateDevice, IoDisconnectInterrupt
The IoCompleteRequest routine indicates that the
caller has completed all processing for a given I/O request and is returning
the given IRP to the I/O Manager.
VOID
IoCompleteRequest(
IN PIRP Irp,
IN CCHAR PriorityBoost
);
Parameters
Irp
Pointer to the IRP to
be completed.
PriorityBoost
Specifies a
system-defined constant by which to increment the runtime priority of the
original thread that requested the operation. This value is
IO_NO_INCREMENT if the original thread requested an operation the driver could
complete quickly (so the requesting thread is not compensated for its assumed
wait on I/O) or if the IRP is completed with an error. Otherwise, the set of PriorityBoost
constants are device-type-specific. See ntddk.h or wdm.h for
these constants.
Return Value
None
Headers
Declared in wdm.h and ntddk.h. Include wdm.h
or ntddk.h.
Comments
When a driver has finished all processing for a given IRP,
it calls IoCompleteRequest. The I/O Manager checks the IRP to determine
whether any higher-level drivers have set up an IoCompletion routine for
the IRP. If so, each IoCompletion routine is called, in turn, until
every layered driver in the chain has completed the IRP.
When all drivers have completed a given IRP, the I/O
Manager returns status to the original requester of the operation. Note that a
higher-level driver that sets up a driver-created IRP must supply an IoCompletion
routine to release the IRP it created.
Never call IoCompleteRequest while holding a spin
lock. Attempting to complete an IRP while holding a spin lock
can cause deadlocks.
Callers of IoCompleteRequest must be running at
IRQL <= DISPATCH_LEVEL.
See Also
The IoGetCurrentIrpStackLocation routine returns a pointer
to the caller’s stack location in the given IRP.
PIO_STACK_LOCATION
IoGetCurrentIrpStackLocation(
IN PIRP Irp
);
Parameters
Irp
Pointer to the IRP.
Return Value
The routine returns a pointer to the I/O stack location
for the driver.
Headers
Declared in wdm.h and ntddk.h. Include wdm.h
or ntddk.h.
Comments
Every driver must call IoGetCurrentIrpStackLocation
with each IRP it is sent in order to get any parameters for the current
request. Unless a driver supplies a dispatch routine for each IRP_MJ_XXX
code that the driver handles, the driver also must check its I/O stack location
in the IRP to determine what operation is being requested.
If a driver is passing the same parameters that it
received to the next-lower driver, it should call IoCopyCurrentIrpStackLocationToNext
or IoSkipCurrentIrpStackLocation
instead of getting a pointer to the next-lower stack location and copying the
parameters manually.
See Also
IO_STACK_LOCATION, IoCallDriver, IoGetNextIrpStackLocation,
IoSetNextIrpStackLocation
The IO_STACK_LOCATION structure defines an I/O stack location,
which is an entry in the I/O stack that is associated with each IRP. Each I/O
stack location in an IRP has some common members and some request-type-specific
members.
typedef struct _IO_STACK_LOCATION {
UCHAR MajorFunction;
UCHAR MinorFunction;
UCHAR Flags;
UCHAR Control;
union
{
//
// Parameters for IRP_MJ_CREATE
//
struct
{
PIO_SECURITY_CONTEXT SecurityContext;
ULONG Options;
USHORT
POINTER_ALIGNMENT FileAttributes;
USHORT
ShareAccess;
ULONG
POINTER_ALIGNMENT EaLength;
} Create;
//
// Parameters for IRP_MJ_READ
//
struct
{
ULONG Length;
ULONG
POINTER_ALIGNMENT Key;
LARGE_INTEGER
ByteOffset;
} Read;
//
// Parameters for IRP_MJ_WRITE
//
struct
{
ULONG Length;
ULONG
POINTER_ALIGNMENT Key;
LARGE_INTEGER
ByteOffset;
} Write;
//
// Parameters for IRP_MJ_QUERY_INFORMATION
//
struct
{
ULONG Length;
FILE_INFORMATION_CLASS POINTER_ALIGNMENT FileInformationClass;
} QueryFile;
//
// Parameters for IRP_MJ_SET_INFORMATION
//
struct
{
ULONG Length;
FILE_INFORMATION_CLASS POINTER_ALIGNMENT FileInformationClass;
PFILE_OBJECT
FileObject;
union {
struct {
BOOLEAN
ReplaceIfExists;
BOOLEAN
AdvanceOnly;
};
ULONG
ClusterCount;
HANDLE DeleteHandle;
};
} SetFile;
//
// Parameters for
IRP_MJ_QUERY_VOLUME_INFORMATION
//
struct
{
ULONG Length;
FS_INFORMATION_CLASS POINTER_ALIGNMENT FsInformationClass;
} QueryVolume;
//
// Parameters for IRP_MJ_DEVICE_CONTROL and IRP_MJ_INTERNAL_DEVICE_CONTROL
//
struct
{
ULONG
OutputBufferLength;
ULONG
POINTER_ALIGNMENT InputBufferLength;
ULONG
POINTER_ALIGNMENT IoControlCode;
PVOID
Type3InputBuffer;
} DeviceIoControl;
//
// Nonsystem service
parameters.
//
// Parameters for
IRP_MN_MOUNT_VOLUME
//
struct
{
PVOID DoNotUse1;
PDEVICE_OBJECT
DeviceObject;
} MountVolume;
//
// Parameters for
IRP_MN_VERIFY_VOLUME
//
struct
{
PVOID DoNotUse1;
PDEVICE_OBJECT
DeviceObject;
} VerifyVolume;
//
// Parameters for Scsi using IRP_MJ_INTERNAL_DEVICE_CONTROL
//
struct
{
struct _SCSI_REQUEST_BLOCK *Srb;
} Scsi;
//
// Parameters for IRP_MN_QUERY_DEVICE_RELATIONS
//
struct
{
DEVICE_RELATION_TYPE Type;
} QueryDeviceRelations;
//
// Parameters for IRP_MN_QUERY_INTERFACE
//
struct
{
CONST GUID
*InterfaceType;
USHORT Size;
USHORT Version;
PINTERFACE
Interface;
PVOID
InterfaceSpecificData;
} QueryInterface;
//
// Parameters for IRP_MN_QUERY_CAPABILITIES
//
struct
{
PDEVICE_CAPABILITIES Capabilities;
}
DeviceCapabilities;
//
// Parameters for
IRP_MN_FILTER_RESOURCE_REQUIREMENTS
//
struct
{
PIO_RESOURCE_REQUIREMENTS_LIST IoResourceRequirementList;
}
FilterResourceRequirements;
//
// Parameters for IRP_MN_READ_CONFIG and IRP_MN_WRITE_CONFIG
//
struct
{
ULONG
WhichSpace;
PVOID Buffer;
ULONG Offset;
ULONG
POINTER_ALIGNMENT Length;
} ReadWriteConfig;
//
// Parameters for IRP_MN_SET_LOCK
//
struct
{
BOOLEAN Lock;
} SetLock;
//
// Parameters for IRP_MN_QUERY_ID
//
struct
{
BUS_QUERY_ID_TYPE IdType;
} QueryId;
//
// Parameters for IRP_MN_QUERY_DEVICE_TEXT
//
struct
{
DEVICE_TEXT_TYPE
DeviceTextType;
LCID
POINTER_ALIGNMENT LocaleId;
} QueryDeviceText;
//
// Parameters for IRP_MN_DEVICE_USAGE_NOTIFICATION
//
struct
{
BOOLEAN InPath;
BOOLEAN Reserved[3];
DEVICE_USAGE_NOTIFICATION_TYPE
POINTER_ALIGNMENT Type;
} UsageNotification;
//
// Parameters for IRP_MN_WAIT_WAKE
//
struct
{
SYSTEM_POWER_STATE PowerState;
} WaitWake;
//
// Parameter for IRP_MN_POWER_SEQUENCE
//
struct
{
PPOWER_SEQUENCE
PowerSequence;
} PowerSequence;
//
// Parameters for IRP_MN_SET_POWER and IRP_MN_QUERY_POWER
//
struct
{
ULONG
SystemContext;
POWER_STATE_TYPE
POINTER_ALIGNMENT Type;
POWER_STATE
POINTER_ALIGNMENT State;
POWER_ACTION POINTER_ALIGNMENT
ShutdownType;
} Power;
//
// Parameters for IRP_MN_START_DEVICE
//
struct
{
PCM_RESOURCE_LIST
AllocatedResources;
PCM_RESOURCE_LIST AllocatedResourcesTranslated;
} StartDevice;
//
// Parameters for WMI Minor IRPs
//
struct
{
ULONG_PTR
ProviderId;
PVOID DataPath;
ULONG
BufferSize;
PVOID Buffer;
} WMI;
//
// Others - driver-specific
//
struct
{
PVOID Argument1;
PVOID Argument2;
PVOID Argument3;
PVOID Argument4;
} Others;
} Parameters;
PDEVICE_OBJECT DeviceObject;
PFILE_OBJECT FileObject;
.
.
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;
Members
MajorFunction
The IRP major function code
indicating the type of I/O operation to be performed.
MinorFunction
A subfunction code for MajorFunction. The PnP Manager, the
Power Manager, file system drivers, and SCSI class drivers set this member for
some requests.
Flags
Request-type-specific values used almost
exclusively by file system drivers. Removable-media device drivers check
whether this member is set with SL_OVERRIDE_VERIFY_VOLUME for read requests to
determine whether to continue the read operation even if the device object’s Flags
is set with DO_VERIFY_VOLUME. Intermediate drivers
layered over a removable-media device driver must copy this member into the I/O
stack location of the next-lower driver in all incoming IRP_MJ_READ requests.
Control
Drivers can check this member to determine
whether it is set with SL_PENDING_RETURNED. Drivers have read-only access to
this member.
Parameters
A union that depends on the major and minor
IRP function code values contained in MajorFunction and MinorFunction.
For more information, see IRP Major Function Codes.
DeviceObject
A pointer to the driver-created DEVICE_OBJECT
structure representing the target physical, logical, or virtual device for
which this driver is to handle the IRP.
FileObject
A pointer to a FILE_OBJECT structure
that represents the file object, if any, that is associated with DeviceObject
pointer.
Headers
Defined in wdm.h and ntddk.h. Include wdm.h
or ntddk.h.
Comments
For each IRP, there is one IO_STACK_LOCATION structure for
each driver in a driver stack. Each
IRP's set of I/O stack locations is appended to the IRP, following the IRP structure.
Every higher-level driver is responsible for setting up
the I/O stack location for the next-lower driver in each IRP. A driver must
call IoGetCurrentIrpStackLocation
to get a pointer to its own stack location for each IRP. Higher-level drivers
can call IoGetNextIrpStackLocation
to get a pointer to the next-lower driver’s stack location.
The higher-level driver must set up the stack location
contents before calling IoCallDriver to
pass an IRP to the lower-level driver. If the driver will pass the input IRP on
to the next lower-level driver, the dispatch routine should call IoSkipCurrentIrpStackLocation
or IoCopyCurrentIrpStackLocationToNext
to set up the I/O stack location of the next-lower driver.
A higher-level driver’s call to IoCallDriver sets
the DeviceObject member to the next-lower-level driver’s target device
object, in the I/O stack location of the lower driver. The I/O Manager passes
each higher-level driver’s IoCompletion
routine a pointer to its own device object when the IoCompletion routine
is called on completion of the IRP.
If a higher-level driver allocates IRPs to make requests
of its own, its IoCompletion routine is passed a NULL DeviceObject
pointer if that driver neither allocates a stack location for itself nor sets
up the DeviceObject pointer in its own stack location of the newly
allocated IRP.
In some cases, a higher-level driver layered over a mass-storage
device driver is responsible for splitting up large transfer requests for the
underlying device driver. In particular, SCSI class drivers must check the Parameters.Read.Length
and Parameters.Write.Length, determine whether the size of the requested
transfer exceeds the underlying HBA’s transfer capabilities, and, if so, split
the Length of the original request into a sequence of partial transfers
to satisfy the original IRP.
See Also
IoCallDriver,
IoCopyCurrentIrpStackLocationToNext,
IoGetCurrentIrpStackLocation,
IoGetNextIrpStackLocation,
IoSetCompletionRoutine,
IoSkipCurrentIrpStackLocation,
IoSetNextIrpStackLocation,
IO_STATUS_BLOCK,
IRP
The IRP structure represents an I/O request packet.
Drivers can use the following members of the IRP structure.
typedef struct _IRP {
.
.
PMDL MdlAddress;
ULONG Flags;
union
{
struct
_IRP *MasterIrp;
.
.
PVOID SystemBuffer;
} AssociatedIrp;
.
.
IO_STATUS_BLOCK IoStatus;
KPROCESSOR_MODE RequestorMode;
BOOLEAN PendingReturned;
.
.
BOOLEAN Cancel;
KIRQL CancelIrql;
.
.
PDRIVER_CANCEL CancelRoutine;
PVOID UserBuffer;
union
{
struct
{
.
.
union
{
KDEVICE_QUEUE_ENTRY
DeviceQueueEntry;
struct
{
PVOID DriverContext[4];
};
};
.
.
PETHREAD Thread;
.
.
LIST_ENTRY ListEntry;
.
.
} Overlay;
.
.
} Tail;
} IRP, *PIRP;
Members
MdlAddress
Pointer to an MDL describing a user buffer, if the driver
is using direct
I/O, and the IRP major function code is one of the following:
The MDL describes an empty buffer that the device or
driver fills in.
The MDL describes a buffer that contains data for the
device or driver.
IRP_MJ_DEVICE_CONTROL
or IRP_MJ_INTERNAL_DEVICE_CONTROL
If the IOCTL code specifies the METHOD_IN_DIRECT transfer
type, the MDL describes a buffer that contains data for the device or driver.
If the IOCTL code specifies the METHOD_OUT_DIRECT transfer type, the
MDL describes an empty buffer that the device or driver fills in.
For more information about the buffers that are associated with
METHOD_IN_DIRECT and METHOD_OUT_DIRECT transfer types in IOCTL codes, see Buffer Descriptions for I/O
Control Codes.
If the driver is not using direct I/O, this pointer is NULL.
Flags
File system drivers use this field, which is read-only for
all drivers. Network and, possibly, highest-level device drivers also might
read this field, which can be set with one or more of the following
system-defined masks:
IRP_NOCACHE
IRP_PAGING_IO
IRP_MOUNT_COMPLETION
IRP_SYNCHRONOUS_API
IRP_ASSOCIATED_IRP
IRP_BUFFERED_IO
IRP_DEALLOCATE_BUFFER
IRP_INPUT_OPERATION
IRP_SYNCHRONOUS_PAGING_IO
IRP_CREATE_OPERATION
IRP_READ_OPERATION
IRP_WRITE_OPERATION
IRP_CLOSE_OPERATION
IRP_DEFER_IO_COMPLETION
AssociatedIrp.MasterIrp
Pointer to the master IRP in an IRP
that was created by a highest-level driver’s call to IoMakeAssociatedIrp.
AssociatedIrp.SystemBuffer
Pointer to a system-space buffer.
If the driver is using buffered
I/O, the buffer's purpose is determined by the IRP major function
code, as follows:
The buffer receives data from the device or driver. The
buffer's length is specified by Parameters.Read.Length in the driver's IO_STACK_LOCATION
structure.
The buffer supplies data for the device or driver. The
buffer's length is specified by Parameters.Write.Length in the driver's
IO_STACK_LOCATION structure.
IRP_MJ_DEVICE_CONTROL
or IRP_MJ_INTERNAL_DEVICE_CONTROL
The buffer represents both the input and output buffers
that are supplied to DeviceIoControl and IoBuildDeviceIoControlRequest.
Output data overwrites input data.
For input, the buffer's length is specified by Parameters.DeviceIoControl.InputBufferLength
in the driver's IO_STACK_LOCATION structure.
For output, the buffer's length is specified by Parameters.DeviceIoControl.OutputBufferLength
in the driver's IO_STACK_LOCATION structure.
For more information, see Buffer Descriptions for I/O
Control Codes.
If the driver is using direct
I/O, the buffer's purpose is determined by the IRP major function
code, as follows:
NULL.
NULL.
IRP_MJ_DEVICE_CONTROL
or IRP_MJ_INTERNAL_DEVICE_CONTROL
The buffer represents the input buffer that is supplied to
DeviceIoControl and IoBuildDeviceIoControlRequest.
The buffer's length is specified by Parameters.DeviceIoControl.InputBufferLength
in the driver's IO_STACK_LOCATION structure.
For more information, see Buffer Descriptions for I/O
Control Codes.
IoStatus
Contains the IO_STATUS_BLOCK
structure in which a driver stores status and information before calling IoCompleteRequest.
RequestorMode
Indicates the execution mode of the
original requester of the operation, one of UserMode or KernelMode. PendingReturned
If set to TRUE, a driver has marked the IRP pending. Each IoCompletion
routine should check the value of this flag. If the flag is TRUE, and if the IoCompletion
routine will not return STATUS_MORE_PROCESSING_REQUIRED, the routine should
call IoMarkIrpPending
to propagate the pending status to drivers above it in the device stack.
Cancel
If set to TRUE, the IRP either is or should be canceled.
CancelIrql
Contains the IRQL at which a driver is running when IoAcquireCancelSpinLock
is called.
CancelRoutine
Contains the entry point for a
driver-supplied Cancel
routine to be called if the IRP is canceled. NULL indicates that the IRP is not
currently cancelable.
UserBuffer
Contains the address of an output buffer if the major
function code in the I/O stack location is IRP_MJ_DEVICE_CONTROL
or IRP_MJ_INTERNAL_DEVICE_CONTROL
and the I/O control code was defined with METHOD_NEITHER.
Tail.Overlay.DeviceQueueEntry
If IRPs are queued in the device queue associated with the
driver’s device object, this field links IRPs in the device queue. These links
can be used only while the driver is processing the IRP.
Tail.Overlay.DriverContext
If IRPs are not queued in the device queue associated with
the driver’s device object, this field can be used by the driver to store up to
four pointers. This field can be used only while the driver owns the IRP.
Tail.Overlay.Thread
Is a pointer to the caller’s thread control block. Higher-level drivers that allocate IRPs for
lower-level removable-media drivers must set this field in the IRPs they
allocate. Otherwise, the FSD cannot determine which thread to notify if the
underlying device driver indicates that the media requires verification.
Tail.Overlay.ListEntry
If a driver manages its own internal queues of IRPs, it
uses this field to link one IRP to the next. These links can be used only while
the driver is holding the IRP in its queue or is processing the IRP.
Headers
Defined in wdm.h and ntddk.h. Include wdm.h or ntddk.h.
Comments
Undocumented members of the IRP structure are reserved, used only by
the I/O Manager or, in some cases, by FSDs.
An IRP is the basic I/O Manager structure used to communicate with
drivers and to allow drivers to communicate with each other. A packet consists
of two different parts:
·
Header,
or fixed part of the packet – This is used by the I/O Manager to store
information about the original request, such as the caller’s device-independent
parameters, the address of the device object upon which a file is open, and so
on. It is also used by drivers to store information such as the final status of
the request.
·
I/O stack locations – Following the header is a set of I/O stack locations,
one per driver in the chain of layered drivers for which the request is bound.
Each stack location contains the parameters, function codes, and context used
by the corresponding driver to determine what it is supposed to be doing. For
more information, see the IO_STACK_LOCATION
structure.
While a higher-level driver might check the value of the Cancel
Boolean in an IRP, that driver cannot assume the IRP will be completed with
STATUS_CANCELLED by a lower-level driver even if the value is TRUE.
See Also
IoCreateDevice,
IoGetCurrentIrpStackLocation,
IoGetNextIrpStackLocation,
IoSetCancelRoutine,
IoSetNextIrpStackLocation,
IO_STACK_LOCATION,
IO_STATUS_BLOCK \
The OpenSCManager function establishes a connection to the
service control manager on the specified computer and opens the specified
service control manager database.
SC_HANDLE OpenSCManager(
LPCTSTR lpMachineName,
LPCTSTR lpDatabaseName,
DWORD dwDesiredAccess
);
Parameters
lpMachineName
[in] Pointer to
a null-terminated string that specifies the name of the target computer.
If the pointer is NULL or points to an empty string, the
function connects to the service control manager on the local computer.
lpDatabaseName
[in] Pointer to
a null-terminated string that specifies the name of the service control manager
database to open. This parameter should be set to SERVICES_ACTIVE_DATABASE.
If it is NULL, the SERVICES_ACTIVE_DATABASE database is opened by default.
dwDesiredAccess
[in] Access to
the service control manager. For a list of access rights, see Service Security and Access
Rights.
Before granting the requested access rights, the system checks the
access token of the calling process against the discretionary access-control
list of the security descriptor associated with the service control manager.
The SC_MANAGER_CONNECT access right is implicitly specified by calling
this function.
Return Values
If the function succeeds, the return value is a handle to the
specified service control manager database.
If the function fails, the return value is NULL. To get extended error
information, call GetLastError.
The following error codes can be set by the SCM. Other error codes can
be set by the registry functions that are called by the SCM.
|
Return Code |
Description |
|
ERROR_ACCESS_DENIED |
The
requested access was denied. |
|
ERROR_DATABASE_DOES_NOT_EXIST |
The
specified database does not exist. |
|
ERROR_INVALID_PARAMETER |
A
specified parameter is invalid. |
Remarks
When a process uses the OpenSCManager function to open a handle
to a service control manager database, the system performs a security check
before granting the requested access.
Only authenticated users are granted the SC_MANAGER_CONNECT,
SC_MANAGER_ENUMERATE_SERVICE, and SC_MANAGER_QUERY_LOCK_STATUS access rights.
Windows 2000/NT: All processes are granted the SC_MANAGER_CONNECT,
SC_MANAGER_ENUMERATE_SERVICE, and SC_MANAGER_QUERY_LOCK_STATUS access rights.
This enables any process to open a service control manager database handle that
it can use in the OpenService, EnumServicesStatus,
and QueryServiceLockStatus
functions.
Only processes with Administrator privileges are able to open a
database handle that can be used by the CreateService
and LockServiceDatabase
functions.
The returned handle is only valid for the process that called the OpenSCManager
function. It can be closed by calling the CloseServiceHandle
function.
Example Code
For an example, see Opening an SCManager
Database.
Client: Included
in Windows XP, Windows 2000 Professional, and Windows NT
Workstation.
Server: Included in Windows Server 2003, Windows 2000 Server,
and Windows NT Server.
Unicode: Implemented as Unicode and ANSI versions.
Header: Declared in Winsvc.h; include Windows.h.
Library: Use Advapi32.lib.
See Also
CloseServiceHandle,
CreateService,
EnumServicesStatus,
LockServiceDatabase,
OpenService, QueryServiceLockStatus,
Service Functions,
Services Overview
The CreateService function creates a service object and adds it
to the specified service control manager database.
SC_HANDLE CreateService(
SC_HANDLE hSCManager,
LPCTSTR lpServiceName,
LPCTSTR lpDisplayName,
DWORD dwDesiredAccess,
DWORD dwServiceType,
DWORD dwStartType,
DWORD dwErrorControl,
LPCTSTR lpBinaryPathName,
LPCTSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPCTSTR lpDependencies,
LPCTSTR lpServiceStartName,
LPCTSTR lpPassword
);
Parameters
hSCManager
[in] Handle to the service
control manager database. This handle is returned by the OpenSCManager
function and must have the SC_MANAGER_CREATE_SERVICE access right. For more
information, see Service Security and Access
Rights.
lpServiceName
[in] Pointer to
a null-terminated string that specifies the name of the service to install.
The maximum string length is 256 characters. The service control manager
database preserves the case of the characters, but service name comparisons are
always case insensitive. Forward-slash (/) and back-slash (\) are invalid
service name characters.
lpDisplayName
[in] Pointer to a null-terminated
string that contains the display name to be used by user interface programs to identify
the service. This string has a maximum length of 256 characters. The name is
case-preserved in the service control manager. Display name comparisons are
always case-insensitive.
dwDesiredAccess
[in] Access to
the service. Before granting the requested access, the system checks the
access token of the calling process. For a list of values, see Service Security and Access
Rights.
dwServiceType
[in] Service
types. This parameter can be one of the following values.
|
Type |
Meaning |
|
SERVICE_FILE_SYSTEM_DRIVER |
File
system driver service. |
|
SERVICE_KERNEL_DRIVER |
Driver
service. |
|
SERVICE_WIN32_OWN_PROCESS |
Service that
runs in its own process. |
|
SERVICE_WIN32_SHARE_PROCESS |
Service
that shares a process with other services. |
If you specify either SERVICE_WIN32_OWN_PROCESS or
SERVICE_WIN32_SHARE_PROCESS, and the service is running in the context of the LocalSystem account,
you can also specify the following type.
|
Type |
Meaning |
|
SERVICE_INTERACTIVE_PROCESS |
The
service can interact with the desktop. For more information, see Interactive Services. |
dwStartType
[in] Service start
options. This parameter can be one of the following values.
|
Type |
Meaning |
|
SERVICE_AUTO_START |
A
service started automatically by the service control manager during system
startup. |
|
SERVICE_BOOT_START |
A device
driver started by the system loader. This value is valid only for driver
services. |
|
SERVICE_DEMAND_START |
A service
started by the service control manager when a process calls the StartService
function. |
|
SERVICE_DISABLED |
A
service that cannot be started. Attempts to start the service result in the
error code ERROR_SERVICE_DISABLED. |
|
SERVICE_SYSTEM_START |
A device
driver started by the IoInitSystem function. This value is valid only
for driver services. |
dwErrorControl
[in] Severity of
the error, and action taken, if this service fails to start. This
parameter can be one of the following values.
|
Value |
Meaning |
|
SERVICE_ERROR_IGNORE |
The
startup program logs the error but continues the startup operation. |
|
SERVICE_ERROR_NORMAL |
The
startup program logs the error and puts up a message box pop-up but continues
the startup operation. |
|
SERVICE_ERROR_SEVERE |
The
startup program logs the error. If the last-known-good configuration is being
started, the startup operation continues. Otherwise, the system is restarted
with the last-known-good configuration. |
|
SERVICE_ERROR_CRITICAL |
The
startup program logs the error, if possible. If the last-known-good configuration
is being started, the startup operation fails. Otherwise, the system is
restarted with the last-known good configuration. |
lpBinaryPathName
[in] Pointer to
a null-terminated string that contains the fully qualified path to the service binary
file. If the path contains a space, it must be quoted so that it is
correctly interpreted. For example, "d:\\my share\\myservice.exe"
should be specified as "\"d:\\my share\\myservice.exe\"".
The path can also include arguments for an auto-start service. For example, "d:\\myshare\\myservice.exe arg1 arg2".
These arguments are passed to the service entry point (typically the main
function).
lpLoadOrderGroup
[in] Pointer to
a null-terminated string that names the load ordering group of which this service
is a member. Specify NULL or an empty string if the service does not
belong to a group.
The startup program uses load ordering groups to load groups of
services in a specified order with respect to the other groups. The list of
load ordering groups is contained in the ServiceGroupOrder value of the
following registry key:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control
lpdwTagId
[out] Pointer to a variable that
receives a tag value that is unique in the group specified in the lpLoadOrderGroup
parameter. Specify NULL if you are not changing the existing tag.
You can use a tag for ordering service startup within a load ordering
group by specifying a tag order vector in the GroupOrderList value of
the following registry key:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control
Tags are only evaluated for driver services that have
SERVICE_BOOT_START or SERVICE_SYSTEM_START start types.
lpDependencies
[in] Pointer to
a double null-terminated array of null-separated names of services or load ordering
groups that the system must start before this service. Specify NULL or
an empty string if the service has no dependencies. Dependency on a group means
that this service can run if at least one member of the group is running after
an attempt to start all members of the group.
You must prefix group names with SC_GROUP_IDENTIFIER so that they can
be distinguished from a service name, because services and service groups share
the same name space.
lpServiceStartName
[in] Pointer to
a null-terminated string that specifies the name of the account under which the
service should run. If the service type is SERVICE_WIN32_OWN_PROCESS,
use an account name in the form DomainName\UserName. The service process
will be logged on as this user. If the account belongs to the built-in domain,
you can specify .\UserName.
If this parameter is NULL, CreateService uses the LocalSystem account.
If the service type specifies SERVICE_INTERACTIVE_PROCESS, the service must run
in the LocalSystem account.
If this parameter is NT AUTHORITY\LocalService, CreateService
uses the LocalService account.
If the parameter is NT AUTHORITY\NetworkService, CreateService uses the NetworkService account.
Windows NT: If the service type is SERVICE_WIN32_SHARE_PROCESS,
you must specify the LocalSystem account.
On later versions of Windows, a shared process can run as any user.
If the service type is SERVICE_KERNEL_DRIVER or
SERVICE_FILE_SYSTEM_DRIVER, the name is the driver object name that the system
uses to load the device driver. Specify NULL if the driver is to use a default
object name created by the I/O system.
lpPassword
[in] Pointer to
a null-terminated string that contains the password to the account name
specified by the lpServiceStartName parameter. Specify an empty
string if the account has no password or if the service runs in the
LocalService, NetworkService, or LocalSystem account. For more information, see
Service Record List.
Passwords are ignored for driver services.
Return Values
If the function succeeds, the return value is a handle to the service.
If the function fails, the return value is NULL. To get extended error
information, call GetLastError.
The following error codes can be set by the service control manager.
Other error codes can be set by the registry functions that are called by the
service control manager.
|
Return Code |
Description |
|
ERROR_ACCESS_DENIED |
The handle
to the SCM database does not have the SC_MANAGER_CREATE_SERVICE access right. |
|
ERROR_CIRCULAR_DEPENDENCY |
A
circular service dependency was specified. |
|
ERROR_DUPLICATE_SERVICE_NAME |
The display
name already exists in the service control manager database either as a
service name or as another display name. |
|
ERROR_INVALID_HANDLE |
The
handle to the specified service control manager database is invalid. |
|
ERROR_INVALID_NAME |
The specified
service name is invalid. |
|
ERROR_INVALID_PARAMETER |
A
parameter that was specified is invalid. |
|
ERROR_INVALID_SERVICE_ACCOUNT |
The user
account name specified in the lpServiceStartName parameter does not
exist. |
|
ERROR_SERVICE_EXISTS |
The specified
service already exists in this database. |
Remarks
The CreateService function creates a service object and
installs it in the service control manager database by creating a key with the same
name as the service under the following registry key:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
Information specified by CreateService, ChangeServiceConfig,
and ChangeServiceConfig2
is saved as values under this key. The following are examples of values stored
for a service.
|
Value |
Description |
|
DependOnGroup |
Load-ordering
groups on which this service depends, as specified by lpDependencies. |
|
DependOnService |
Services
on which this service depends, as specified by lpDependencies. |
|
Description |
Description
specified by ChangeServiceConfig2 . |
|
DisplayName |
Display
name specified by lpDisplayName. |
|
ErrorControl |
Error
control specified by dwErrorControl. |
|
FailureActions |
Failure
actions specified by ChangeServiceConfig2 . |
|
Group |
Load
ordering group specified by lpLoadOrderGroup. |
|
ImagePath |
Name of
binary file, as specified by lpBinaryPathName. |
|
ObjectName |
Account
name specified by lpServiceStartName. |
|
Start |
When to
start service, as specified by dwStartType. |
|
Tag |
Tag
identifier specified by lpdwTagId. |
|
Type |
Service
type specified by dwServiceType. |
Setup programs and the service itself can create additional subkeys
for service-specific information.
The returned handle is only valid for the process that called CreateService.
It can be closed by calling the CloseServiceHandle
function.
If you are creating services that share a process, avoid calling
functions with process-wide effects, such as ExitProcess.
In addition, do not unload your service DLL.
Example Code
For an example, see Installing a Service.
Client: Included
in Windows XP, Windows 2000 Professional, and Windows NT
Workstation.
Server: Included in Windows Server 2003, Windows 2000 Server,
and Windows NT Server.
Unicode: Implemented as Unicode and ANSI versions.
Header: Declared in Winsvc.h; include Windows.h.
Library: Use Advapi32.lib.
See Also
Services Overview,
Service Functions,
ChangeServiceConfig,
ChangeServiceConfig2,
CloseServiceHandle,
ControlService,
DeleteService,
EnumDependentServices,
OpenSCManager,
QueryServiceConfig,
QueryServiceObjectSecurity,
QueryServiceStatus,
SetServiceObjectSecurity,
StartService
![]()
OpenService
The OpenService function opens an existing service.
SC_HANDLE OpenService(
SC_HANDLE hSCManager,
LPCTSTR lpServiceName,
DWORD dwDesiredAccess
);
Parameters
hSCManager
[in] Handle to the service
control manager database. The OpenSCManager
function returns this handle.
lpServiceName
[in] Pointer to
a null-terminated string that specifies the name of the service to open.
The maximum string length is 256 characters. The service control manager
database preserves the case of the characters, but service name comparisons are
always case insensitive. Forward-slash (/) and backslash (\) are invalid
service name characters.
dwDesiredAccess
[in] Access to
the service. For a list of access rights, see Service Security and Access
Rights.
Before granting the requested access, the system checks the access
token of the calling process against the discretionary access-control list of
the security descriptor associated with the service object.
Return Values
If the function succeeds, the return value is a handle to the service.
If the function fails, the return value is NULL. To get extended error
information, call GetLastError.
The following error codes can be set by the service control manager. Others
can be set by the registry functions that are called by the service control
manager.
|
Return Code |
Description |
|
ERROR_ACCESS_DENIED |
The
handle does not have access to the service. |
|
ERROR_INVALID_HANDLE |
The
specified handle is invalid. |
|
ERROR_INVALID_NAME |
The
specified service name is invalid. |
|
ERROR_SERVICE_DOES_NOT_EXIST |
The
specified service does not exist. |
Remarks
The returned handle is only valid for the process that called OpenService.
It can be closed by calling the CloseServiceHandle
function.
Example Code
For an example, see Starting a Service.
Client: Included
in Windows XP, Windows 2000 Professional, and Windows NT
Workstation.
Server: Included in Windows Server 2003, Windows 2000 Server,
and Windows NT Server.
Unicode: Implemented as Unicode and ANSI versions.
Header: Declared in Winsvc.h; include Windows.h.
Library: Use Advapi32.lib.
See Also
ChangeServiceConfig,
CloseServiceHandle,
ControlService,
CreateService,
DeleteService,
EnumDependentServices,
OpenSCManager,
QueryServiceConfig,
QueryServiceObjectSecurity,
QueryServiceStatus,
Service Functions,
Services Overview,
SetServiceObjectSecurity,
StartService
The StartService function starts a service.
BOOL StartService(
SC_HANDLE hService,
DWORD dwNumServiceArgs,
LPCTSTR* lpServiceArgVectors
);
Parameters
hService
[in] Handle to the service. This
handle is returned by the OpenService
or CreateService
function, and it must have the SERVICE_START access right. For more
information, see Service Security and Access
Rights.
dwNumServiceArgs
[in] Number of
strings in the lpServiceArgVectors array. If lpServiceArgVectors
is NULL, this parameter can be zero.
lpServiceArgVectors
[in] Pointer to
an array of pointers to null-terminated strings to be passed to a service as
arguments. Driver services do not receive these arguments. If no
arguments are passed to the service, this parameter can be NULL. The service
accesses these arguments through its ServiceMain
function. The first argument (argv[0]) is the name of
the service by default, followed by the arguments, if any, in the lpServiceArgVectors
array.
Return Values
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error
information, call GetLastError.
The following error codes can be set by the service control manager.
Others can be set by the registry functions that are called by the service
control manager.
|
Return Code |
Description |
|
ERROR_ACCESS_DENIED |
The
handle does not have the SERVICE_START access right. |
|
ERROR_INVALID_HANDLE |
The
handle is invalid. |
|
ERROR_PATH_NOT_FOUND |
The
service binary file could not be found. |
|
ERROR_SERVICE_ALREADY_RUNNING |
An
instance of the service is already running. |
|
ERROR_SERVICE_DATABASE_LOCKED |
The
database is locked. |
|
ERROR_SERVICE_DEPENDENCY_DELETED |
The
service depends on a service that does not exist or has been marked for
deletion. |
|
ERROR_SERVICE_DEPENDENCY_FAIL |
The
service depends on another service that has failed to start. |
|
ERROR_SERVICE_DISABLED |
The
service has been disabled. |
|
ERROR_SERVICE_LOGON_FAILED |
The
service could not be logged on. This error occurs if the service was started
from an account that does not have the "Log on as a service" right. |
|
ERROR_SERVICE_MARKED_FOR_DELETE |
The
service has been marked for deletion. |
|
ERROR_SERVICE_NO_THREAD |
A thread
could not be created for the service. |
|
ERROR_SERVICE_REQUEST_TIMEOUT |
The
process for the service was started, but it did not call StartServiceCtrlDispatcher,
or the thread that called StartServiceCtrlDispatcher may be blocked in
a control handler function. |
Remarks
When a driver service is started, the StartService function does
not return until the device driver has finished initializing.
When a service is started, the service control manager spawns the
service process, if necessary. If the specified service shares a process with
other services, the required process may already exist. The StartService
function does not wait for the first status update from the new service,
because it can take a while. Instead, it returns when the service control
manager receives notification from the service control dispatcher that the ServiceMain
thread for this service was created successfully.
The service control manager sets the following default status values
before returning from StartService:
·
Current
state of the service is set to SERVICE_START_PENDING.
·
Controls accepted is set to none (zero).
·
The
CheckPoint value is set to zero.
·
The
WaitHint time is set to 2 seconds.
The calling process can determine if the new service has finished its initialization
by calling the QueryServiceStatus
function periodically to query the service's status.
A service cannot call StartService during initialization. The reason
is that the service control manager locks the service control database during
initialization, so a call to StartService will block. Once the service
reports to the service control manager that it has successfully started, it can
call StartService.
Example Code
For an example, see Starting a Service.
Client: Included
in Windows XP, Windows 2000 Professional, and Windows NT
Workstation.
Server: Included in Windows Server 2003, Windows 2000 Server,
and Windows NT Server.
Unicode: Implemented as Unicode and ANSI versions.
Header: Declared in Winsvc.h; include Windows.h.
Library: Use Advapi32.lib.
See Also
ControlService,
CreateService,
OpenService, QueryServiceStatus,
Service Functions,
Services Overview,
ServiceMain
The CreateFile function creates or opens a file, directory,
physical disk, volume, console buffer, tape drive, communications resource,
mailslot, or pipe. The function returns a handle that can be used to access the
object.
Windows Me/98/95: You cannot open a directory, physical disk, or
volume using CreateFile.
HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
Parameters
lpFileName
[in] Pointer to
a null-terminated string that specifies the name of the object to create or
open.
In the ANSI version of this function, the name is limited to MAX_PATH
characters. To extend this limit to 32,767 wide characters, call the Unicode
version of the function and prepend "\\?\"
to the path. For more information, see Naming a File.
Windows Me/98/95: This string must not exceed
MAX_PATH characters.
dwDesiredAccess
[in] Access to
the object. For a list of values, see File Security and Access
Rights. You cannot request an access mode that conflicts with the
sharing mode specified in a previous open request whose handle is still open.
If this parameter is zero, the application can query file and device
attributes without accessing the device. This is useful if an application wants
to determine the size of a floppy disk drive and the formats it supports
without requiring a floppy in the drive. It can also be used to test for the file's or directory's existence without opening it for read
or write access.
dwShareMode
[in] Sharing
mode of the object. You cannot request a sharing mode that conflicts
with the access mode specified in a previous open request whose handle is still
open.
If this parameter is zero and CreateFile succeeds, the object
cannot be shared and cannot be opened again until the handle is closed. For
more information about sharing violations, see the Remarks section.
To enable other processes to share the object while your process has
it open, use a combination of one or more of the following values to specify
the type of access they can request when they open the object. These sharing
options remain in effect until you close the handle to the object.
|
Value |
Meaning |
|
FILE_SHARE_DELETE |
Enables
subsequent open operations on the object to request delete access. Otherwise,
other processes cannot open the object if they request delete access. If the object has already been opened with delete
access, the sharing mode must include this flag. Windows Me/98/95: This flag is not supported. |
|
FILE_SHARE_READ |
Enables subsequent
open operations on the object to request read access. Otherwise, other
processes cannot open the object if they request read access. If the object has already been opened with read access,
the sharing mode must include this flag. |
|
FILE_SHARE_WRITE |
Enables
subsequent open operations on the object to request write access. Otherwise,
other processes cannot open the object if they request write access. If the object has already been opened with write access,
the sharing mode must include this flag. |
lpSecurityAttributes
[in] Pointer to
a SECURITY_ATTRIBUTES
structure that determines whether the returned handle can be inherited by child
processes. If lpSecurityAttributes is NULL, the handle cannot be
inherited.
The lpSecurityDescriptor member of the structure specifies a security descriptor
for the object. If lpSecurityAttributes is NULL, the object gets a
default security descriptor. The ACLs in the default security descriptor for a
file or directory are inherited from its parent directory. Note that the target
file system must support security on files and directories for this parameter
to have an effect on them. (This is indicated when GetVolumeInformation
returns FS_PERSISTENT_ACLS.) CreateFile ignores lpSecurityDescriptor
when opening an existing file, but continues to use the other structure
members.
dwCreationDisposition
[in] Action to
take on files that exist, and which action to take when files do not exist.
For more information about this parameter, see the Remarks section. This
parameter must be one of the following values.
|
Value |
Meaning |
|
CREATE_ALWAYS |
Creates
a new file. If the file exists, the function overwrites the file, clears the existing
attributes, combines the specified file attributes and flags with
FILE_ATTRIBUTE_ARCHIVE, but does not set the security descriptor specified by
the SECURITY_ATTRIBUTES structure. |
|
CREATE_NEW |
Creates
a new file. The function fails if the specified file already exists. |
|
OPEN_ALWAYS |
Opens
the file, if it exists. If the file does not exist, the function creates the
file as if dwCreationDisposition were CREATE_NEW. |
|
OPEN_EXISTING |
Opens the
file. The function fails if the file does not exist. For a discussion of why you should use OPEN_EXISTING for
devices, see Remarks. |
|
TRUNCATE_EXISTING |
Opens
the file and truncates it so that its size is zero bytes. The calling process
must open the file with the GENERIC_WRITE access right. The function fails if
the file does not exist. |
dwFlagsAndAttributes
[in] File attributes and flags.
The following file attributes and flags are used only for file objects,
not other types of objects created by CreateFile.
When CreateFile opens an existing file, it combines the file
flags with existing file attributes, and ignores any supplied file attributes.
This parameter can include any combination of the file attributes
(noting that all other file attributes override FILE_ATTRIBUTE_NORMAL).