Just the other day, while walking through the hundred acre wood, I was accosted by the local fauna.
“Excuse me” asked a small pink pig. “B-B-B-But do you know anything about Windows Device Drivers? Because, if you do, our friend here needs some help,” he said, pointing to a rather roly-poly teddy bear.
“What kind of help?” I asked the little bear.
“Well,” replied the little bear, “I am a Bear of Little Brain. I just wrote a Windows Device Driver, but I don’t know how to install it.”
“What you need to do is write an .INF file, which describes your driver and the device it controls, to the Windows operating system,” I said. “Uh, by the way, what device does your driver control?”
“It is a hunny filter. It removes dirt, bees, and other impurities in hunny. It is my own invention,” he said rather proudly.
“Hmm,” I thought to myself, “not bad for a ‘Bear of Little Brain’.”
“Do you think, that is to say, do you know how to write one of these ‘Dot Eye Eneff’ files?” the bear asked.
“Sure, why don’t we all sit down right here, and I’ll tell you all about .INF files—Hey, where’s everyone going? Come back! They’re not that bad!”
The small group of animals slowly reappeared, one by one, with more than a little trepidation in their eyes.
“Don’t worry, .INF files aren’t nearly as bad as you think,” I said, trying to soothe their fears.
“W-W-Well, that’s n-n-not what m-m-most p-p-people say,” said the little pink pig, clearly frightened.
“Calm yourself my little friend, nervous eaters don’t grow up to be big tasty—er, healthy pigs,” I said.
The little pig calmed down noticeably, but still regarded me with suspicion.
Purpose of .INF Files
This article will describe the basics of .INF files and how they are used in the installation of a device driver. Of course, the definitive reference material on .INF files can be found on the Microsoft web site, here. Ask just about any device driver writer, “What is the worst part of writing Windows device drivers?” and most will reply, “Writing .INF files!” The reason for this is that the documentation on .INF files in the WDK has not provided a procedural approach to describing .INF files, such as, “If you want to accomplish X, then do Y followed by Z.”
An .INF file can do many things; however, 97% of all .INF files really only perform three tasks:
- Identify the driver for a particular device. This is done using one or more Hardware IDs or Compatible IDs. The system device installer will take the Hardware IDs and Compatible IDs that were reported for a device (by its bus driver), and try to find an exact character for character match in an .INF file. When a match is found, then the system device installer knows that the .INF file describes the driver(s) for the device.
- Copy files from the installation medium to the system. In addition to the driver binary (the .SYS file) and catalog file (the .CAT file, which contains the digital signatures of the binaries and of the .INF file), driver packages may also include DLLs, co-installers, applications, or any other type of file.
- Add entries to the registry. This describes the device and its relationship to other devices, provides for device- or driver-specific configuration information, and describes the driver “service” to the service control manager.
That’s it. Sounds simple, doesn’t it? Actually, it is pretty simple (no, I’m not kidding!).
“You’re right,” replied the little bear, “even a Bear of Little Brain understands that.”
The most confusing aspects of .INF files are usually:
- The .INF file is not “run” from the beginning to the end; individual sections in the .INF file are “run” based upon the phase of the installation process.
- Most of the sections in the .INF file are actually part of a well-defined hierarchy. This fact is obscured by the hierarchy having been squashed into a flat text file.
- Some sections in the .INF file, like [Version] and [Strings], have predefined names. The others have names that are created by the author of the .INF file and are established by directives in earlier sections in the hierarchy (not necessarily earlier in the file).
- Section names can be “decorated” with suffixes containing directives, which tell the system device installer if and when to process a particular section.
Structure of .INF files
An .INF file consists of tables (called “sections”) and comments. Each section has a name, which is enclosed in square brackets, e.g.
[SectionName] [another_section_name]
All section names are case-insensitive, and may in theory be up to 255 non-whitespace ANSI or Unicode characters in length. Spaces, tabs, other control characters, and [%];”\ are not allowed (well, in some cases some of those characters are allowed; but to simplify things, let’s just not use them). To separate words in a section name, do not use periods (“.”), which are reserved to Microsoft; instead, use the underscore (“_”) or hyphen (“-“), e.g.
[My_model-section]
Each section contains zero or more “directives” (yes, that means that sections may be empty). The directives tell the system device installer what to do, e.g. copy a file, or add an entry to the registry. The comment character is the semi-colon (“;”), which can appear anywhere on a line.
There are no conditional statements in the .INF language, which means there are no “IF-THEN-ELSE” or other similar control flow constructs. However, section names can be “decorated” with a platform-dependent suffix which the system device installer will preferentially select when installing on the specified platform, e.g.
[My_model-section.NTAMD64]
This is a section that will only be “run” on x64 systems (AMD and Intel 64-bit CPUs).
As I mentioned earlier, the sections are not “run” in the order in which they appear in the file. Sections are “run” based upon the installation phase; therefore, sections may appear in any order in the file. By convention, though, the [Version] section is always first, and the [Strings] section is always last.
An .INF file defines several strings that may be displayed to the user during the driver installation. There is a string substitution capability within .INF files that works very similarly to the #define macro in C. String substitution macros are defined in a section called [Strings]. The directives in the section define the string macros, e.g.
[Strings] my_company = "California Death Beams and Stuff, Inc." my_device = "X-15 Cruise Basselope Stealth Weapons Platform"
The system device installer will perform the macro substitution wherever it finds a macro invocation of the form %macro-name%, e.g.
[Manufacturer] %my_company% = CDBS_products-install
Most people in the world who use computers generally have an “English-to-their-language” dictionary handy, but non-English speaking people would much rather have everything on their computer output in their own language. You can declare all the printable strings in your .INF file using these macros, and the system device installer will chose the correct strings based upon what language the user has installed on the system. You do this by providing multiple [Strings] sections. Each string section name may be decorated (decorated names were discussed earlier) with a Locale ID. The Locale ID follows the same form used in the SDK, and consists of a four-digit hexadecimal number with the low ten bits indicating the primary language, and the upper six bits indicating the sublanguage. For example, U.S. English is 0409. The definitions of the language and sublanguage IDs are in the SDK. For example:
[Strings] ; Default, US English one = "One" two = "Two" [Strings.040c] ; French (Standard) one = "Un" two = "Deux" [Strings.080a] ; Spanish (Mexico) one = "Uno" two = "Dos"
With the above [Strings} sections, wherever the system device installer encounters %one% or %two% in the .INF file, it will substitute it with the string from one of the [Strings] sections. If the user has installed some other language that isn’t specified in a [Strings] section, the system will choose the default strings section (the one without the decoration) for the macro definitions. An .INF file that supports multiple languages is often referred to as having been “localized.”
Commonly-used sections
If you look at the WDK documentation on .INF files, you will find that there are twenty-three section types understood by the system device installer (as of this writing, new sections are added occasionally). Each class installer, e.g. Network, Video, Storage, etc., can add its own section types, or its own directives to existing sections. Twenty-three section types may seem a bit daunting, but 97% of all .INF files can generally get away with about a dozen or so section types, which we’ll briefly cover. Of course, the WDK is the definitive source for information on .INF files.
As I mentioned earlier, most of the sections in the .INF file are ordered in a hierarchy, as seen here:
The sections on the left are not part of the hierarchy, and are essentially “global” throughout the .INF file (G for global, G for green; I know, it sounds like “Sesame Street,” but it works). The root of the hierarchy is the [Manufacturer] section.
Some of the section names are required, because the sections are called by those names from the system device installer (e.g. Manufacturer, Strings, Version, .Service, .HW, etc.). Other section names can be named whatever you like, because they are only referenced from within the .INF file by you.
Following are the most commonly-used sections and their most commonly-used directives.
“Wait a minute,” said the little bear, there’s something missing.”
“What? Lets see, Version, ClassInstall32, Control Flags, SourceDisksNames, SourceDisksFiles, DestinationDirs, Manufacturer, Models, DDInstall, AddReg, CopyFiles, .Services, .HW, and Strings. Yep, that’s all of them. What’s missing?” I asked.
“Hunny.”
“Hunny?” I asked.
“Yes,” the little bear replied, “you forgot Hunny.”
“Um… Microsoft doesn’t have a Hunny section in .INF files,” I said.
“Why not?” asked a puzzled little bear.
“Well, .INF files are used for installing device drivers, and you don’t install Hunny in a computer system.”
“Everything’s better with Hunny,” he said rather smugly.
“Yes, I’m… sure it is, but Hunny is rather sticky, and—”
“The stickiness is the best part,” he interrupted.
“O.K., but I don’t think it will do the circuits much good, and for the record, I would prefer that you not install Hunny in any of my systems!” I said rather strongly.
“O.K.,” he grudgingly agreed.
“Alright, can I get on with the lesson?” I asked.
“Yes, please,” he replied.
Sections outside the hierarchy
Version
This is the standard header for all .INF files, and it specifies which operating system and class installer can parse this .INF file.
; ; Identify this .INF file ; [Version] Signature = "$Windows NT$" ; Required value Class = %my_class% ; My class name ClassGUID = {30320101-C613-11d2-9647-0120AFEB03E0}; My device setup class GUID Provider = %my_company% ; My company CatalogFile = CDBS_Products.cat ; File containing signatures of files in driver pkg DriverVer = 04/01/2014,1.0.121.1 ; Driver date & version
Signature specifies which operating system families can parse this .INF file. In the deep dark past, Windows 95 and Windows 98 were supported. Now, always use “$Windows NT$.” In very old .INF files you may see the string “$Chicago$”, which meant Windows 98 and later, and Windows 2000 and later. (Why Chicago? Who knows? I do know that “‘Chicago” was the code name for Windows 95, but what that has to do with this I can’t say.)
Class specifies the name of the device setup class the device is a member of. See DevGUID.H for a list of predefined classes and their GUIDs. In this case, I created my own class, the “MyClass” class. If you create your own class name, then you must also have a ClassInstall32 section.
ClassGUID specifies the device setup class GUID for this .INF file. The Class and ClassGUID directives better match, or the device install will fail. If you create your own class, create a new GUID using the GUIDGEN tool in the WDK or SDK. (Don’t copy the class GUID from my file!) If you are using a known class, e.g. Mouse, then look up the GUID in DevGUID.H. Note that the device setup class is not the same thing as the device interface class; the device setup class specifies which class installer knows how to install your driver, while the device interface class identifies the set of I/O requests that are handled by your driver.
Provider identifies who wrote the .INF file. Generally, this will be the company that produced the device, but it is often “Microsoft”, because they write a lot of .INF files for vendors.
CatalogFile specifies the file containing the digital signature of the files in the driver package, which is typically just the driver binary (.SYS file) and the .INF file. This file is generated by Visual Studio when your driver package is built. If your driver is signed by Microsoft, they will provide you with the .CAT file. The file name for the .CAT file is usually the same as the file name for the .INF file, e.g. MyProduct.INF and MyProduct.CAT.
DriverVer identifies the date the driver was built and its version number. This directive is filled in automatically by Visual Studio when you build your driver package. The system device installer uses this information to select which .INF file to use, when it finds more than one. There is a rather complicated mechanism used by Windows for selecting which driver to load for a particular device, and is described here.
Note: Older .INF files that shipped with Windows will have a LayoutFile directive in the Version section. This should not be in your .INF file because your driver does not ship with Windows (at least, currently). If you are starting from an existing .INF file you found in the system, remove this directive.
ClassInstall32
This section is only required if you creating your own setup class instead of using one of the pre-defined classes. You should try to use the pre-defined classes unless your device really does not fit into any of them. When you create your own class, Device Manager will display your class name, and your devices beneath it. I’m not going to go into all the details here on all the possible values; you can find that information here.
; ; This section creates our own setup class ; [ClassInstall32] AddReg = MyClass-ClassReg [MyClass-ClassReg] HKR,,, 0, %ClassName% HKR,, Icon,, -5
Most of the time, you’ll just have an AddReg directive in your ClassInstall32 section that points to another section – named by the INF author – containing values to add to the registry. In this case, these values provide the class name (referencing a string defined in the Strings section) and an icon that will be displayed in Device Manager (the -5 identifies the fifth icon in the SetupAPI.DLL file). If you have a setup class installer DLL, then you would provide its name in this section, too.
ControlFlags
; ; This section prevents the user from being able to load ; the driver unless the hardware is present ; [ControlFlags] ExcludeFromSelect = *
ExcludeFromSelect is the most common directive used in a ControlFlags section. It contains a list of devices installed by this .INF file that reside on enumerable buses (buses that can uniquely identify each device on its bus, e.g. PCI and USB, but not ISA). This directive informs the Add Device Wizard to not allow the user to select any of the specified devices for manual installation. Instead, the driver will be loaded when the device is detected by the PnP manager. If all devices installed by the .INF file are on enumerable buses, then you may specify an asterisk; otherwise, specify the hardware ID or compatible ID of each device, separated by a comma.
SourceDisksNames
This section identifies the installation disks that contain the files to be copied to the system during the installation process. “Installation disks?” This .INF file format was developed when it was still common for product software to be supplied on floppy disks, and often they needed more than one. To completely explain the format (or to help you understand ancient .INF files) we’ll give an example:
; ; Identify the installation media ; [SourceDisksNames] 1 = "MyProduct Installation Disk #1" 2 = "MyProduct Installation Disk #2"
The directives for the SourceDisksNames section are different from the directives we have discussed up to this point. The format of the directive is:
disk-id = disk-description[,[tag-or-cab-file],[unused][,path][,flags][,tag-file]]
disk-id is simply an ordinal number for a disk. Generally, disks are numbered starting at one.
disk-description is any string that identifies the disk. This string will be displayed to the user when the system device installer requires the disk to be inserted into the system. It should be unique for each disk, and your production facility should put the exact same string on a label on the disk.
tag-or-cab-file is either the name of a file known to be only on that disk (tag file), or the name of a cabinet file (.CAB) containing the installation files.
unused is not used in Windows 2000 and later.
path allows a subdirectory on the disk to be specified. The class installer will ignore everything else on the disk.
flags when set to 0x10, forces tag-or-cab-file to be used as a cabinet file, and tag-file as a tag file within the .cab file.
Note: SourceDisksNames and SourceDisksFiles are not required for your .INF file to function properly; however, Visual Studio 2013 will fail building a driver package if they are not present.
SourceDisksFiles
This section identifies all the files that will be copied to the system during the installation process, and identifies the installation media they reside on, using the disk-ids established in the SourceDisksNames section.
; ; Identify the files on the installation media ; [SourceDisksFiles] Firmware.hex = 1 ; Device firmware on disk #1 Basselope.sys = 2 ; Device driver on disk #2
Like the SourceDisksNames section, the directives here are different from those in most other section types. In this case, for each file that is to be copied to the system, the disk on which the file resides is identified. This section works in conjunction with the SourceDisksFiles section. The format of the directive is:
filename = disk-id[,[sub-directory][,size]]
filename is the name of a file on one of the installation disks.
disk-id is the disk the contains the file, and is one of the disk-ids (ordinal numbers) listed in the SourceDisksNames section.
sub-directory identifies a sub-directory on the disk in which the file is located.
size is the uncompressed size of the file in bytes; this is generally not used.
DestinationDirs
This section specifies the directories on the system to which the files named in SourceDiskFiles will be copied, by providing a list of section names that contain a list of files and a directory number.
; ; Identify where the files will be copied to ; [DestinationDirs] Driver-Files = 12 [Driver-Files] driver.sys different_file.dat
The format for directives in the DestinationDirs section is:
section-name = directory-id[,sub-directory]
section-name is the name of a CopyFiles section (to be described later) created by the .INF file writer. This section contains a list of file names (that are also listed in the SourceDisksFiles section, if you have one) that will be copied to a specific directory.
directory-id is a number, a predefined identifier for a system directory. The most commonly used directory-ids for driver writers are:
12 = %SystemRoot%\System32\Drivers
11 = %SystemRoot%\System32
In this case, %SystemRoot% is a predefined environment variable (visible with the “SET” command at a command prompt) that points to your system directory; it is not a macro in the strings section.
sub-directory identifies a sub-directory of the directory identified by directory-id. For example, “11,Drivers” means the same thing as “12”.
Alternatively, the reserved word “DefaultDestDir” may be used, which specifies the destination directory for all files that do not have their destination directory specified explicitly.
; ; Identify where the files will be copied to ; [DestinationDirs] DefaultDestDir = 12
Strings
As described previously, this section provides macro definitions for strings used within the .INF file.
; ; String substitution definitions ; [Strings] my_class = "CDBS Weapons" company_name = "California Death Beams and Stuff, Inc." my_device = "X-15 Basselope Stealth Weapons Platform" service_desc = "X-15 Basselope driver"
The format of this section is:
macro-name = “some string”
macro-name is the name of the macro defined for the string, and is dereferenced elsewhere in the .INF file by surrounding it with percent (“%”) signs, e.g. %macro-name%.
“some string” is the definition of the macro, and will replace the invocation of the macro where ever it appears in the .INF file.
The hierarchy of sections
The rest of the sections to be described are part of the hierarchy. The root of the tree is the Manufacturer section.
Manufacturer
The manufacturer section is very similar to a directory; it lists the manufacturers whose devices are supported by this .INF file. For third parties it is very rare for there to be more than one manufacturer listed; however, .INF files written by Microsoft may have a dozen or more manufacturers supported by a single .INF file.
; ; The root of the hierarchy for this file ; [Manufacturer] %MfgName% = CDBS_products-install
The syntax for this section is also different from any other section in the .INF file. If you look at the directive, it appears that a quoted string (via the macro substitution) is being assigned the value of some identifier. That is not the case. The equal sign (“=”) here is not used as an assignment operator, but as a separator (a rather poor choice for a separator, but it is a separator nonetheless). This section is used during the early phase of the installation process when the system device installer is searching the .INF files for a matching Hardware ID or Compatible ID. The format of each directive in this section is:
manufacturer-name = models-section-name
manufacturer-name is a string (usually defined in the Strings section) that identifies the manufacturer of the device.
models-section-name is the name of a section (created by the .INF file author) that contains a list of the device models for this manufacturer that supported by this .INF file. That section is one of possibly several Models sections in the .INF file.
The Manufacturer section is the only section in the hierarchy that has a completely predefined name.
Models
This section identifies the various device models for a particular manufacturer that are supported by this .INF file. The name of this section is determined by the .INF file author, and is defined in the Manufacturer section. The fact that it is defined in the Manufacturer section is what makes it a Models section.
; ; Supported devices ; [CDBS_products-install] %device1-name% = DDInstall-device1, hardware_ID, hardware_ID %device2-name% = DDInstall-device2, compatible_ID
The directives in this section are similar to those found in the Manufacturer section. The format of this section is:
device-description = DDinstall-section-name, PNP-id[,PNP-id]…
device-description is a string (usually via a macro) that describes the device.
DDinstall-section-name is the name of a DDinstall section created by the .INF file author, which contains the directives which in turn name other sections that install the driver for the device.
PNP-id is either a hardware ID or compatible ID.
In the following example, the system device installer would start at the beginning of the Manufacturer section when searching for an .INF file for a particular device. It would then search the .INF file for the models section [California_Death_Beams], which contains a list of the devices from that manufacturer that are supported by this .INF file. If none of the hardware IDs or compatible IDs in the [California_Death_Beams] models section match the PnP IDs the installer is looking for, it will then look at the next entry in the Manufacturer section. It would then search the .INF file for the [Texas_Techo_Gizmo] models section, and examine the hardware IDs and compatible IDs listed.
If the device installer finds a match with any of the PnP IDs listed in either of the Models sections, then it has determined that this .INF file supports the device, and it will then process the directives in the DDinstall section named in the directive that defined the matched PnP ID.
[Manufacturer] %cdbs% = California_Death_Beams %ttg% = Texas_Techno_Gizmo [California_Death_Beams] %phaser% = install-phaser, pci\ven_9876&dev_1234 %blaster% = install-blaster, pci\ven_9876%dev_5678 [Texas_Techno_Gizmo] %bass% = install-bass, dog\type_basselope&model_2003 [install-phaser] (...) [install-blaster] (...) [install-bass.NTx86] (...) [install-bass.NTAMD64] (...) [strings] cdbs = "California Death Beams and Stuff, Inc." ttg = "Texas Techo Gizmo, LLC." phaser = "Hand-held phaser, model 1234" blaster = "Hand-held blaster, model 5678" bass = "X-15 Basselope Stealth Weapons Platform"
As illustrated above, there can be several different Models sections, generally one per manufacturer named in the Manufacturer section. In general, the rest of the hierarchy will occur for every Models section, but it is possible for some of the lower-level sections to be used “under” somewhere in the tree “under” more than one Models section. In other words, the tree structure is not strictly enforced.
At this point one of the animals coughed slightly. “Yes? Do you have a question?” I asked.
“Are you going to cover every possible option in all these directives?” I could see that the group was getting restless.
“Oh, no!” I said. “The WDK documentation covers all of those low-level details very well. I’m mostly just trying to show you the structure, how it all fits together. You can get all the little things from the doc’, if and when you need it.”
Seeing that they were greatly relieved, I pressed on.
DDInstall
This section (also called DDInstallSection in the WDK documentation) lists the actions required to install the driver for the device, by naming other sections for each specific action (copying files, adding registry entries, etc.). The name of this section is determined by the .INF file writer, and is defined in the Models section.
; ; Install the driver for the model 1234 hand-held Phaser device ; [install-phaser] CopyFiles = Phaser-Driver-Files AddReg = Phaser-Registry ; ; Specify which files to copy ; [Phaser-Driver-Files] phaser.sys firmware.hex [Phaser-Registry] HKR,,DebugLevel,0x00010001,2 HKR,,FirmwareFile,0x00000000,\SystemRoot\System32\Drivers\firmware.hex HKR,,AutoloadFirmware,0x00010001,1
This section supports many different directives; the most common are CopyFiles and AddReg.
The CopyFiles directive specifies the name of one or more sections (created by the .INF file author) that contain a list of file names (previously specified in the SourceDisksFiles section, if you have one) to be copied.
The AddReg directive specifies the name of one or more sections (created by the .INF file author) that contain a list of entries to add to the registry.
.Services
After the DDInstall section has been run, the system device installer will search the .INF file for a section name that is identical to the DDInstall section, with the suffix “.Services”. The .Services section is used to add entries to the “services” key in the registry (HKLM\System\CurrentControlSet\Services\<driver service name>), describing the driver to the service control manager.
; ; Add the driver service for the phaser ; [install-phaser.Services] AddService = Phaser, 0x00000002, Phaser-Add-Service ; ; Describe the service ; [Phaser-Add-Service] DisplayName = %ServiceDesc% ServiceType = 1 ; SERVICE_KERNEL_DRIVER StartType = 3 ; SERVICE_DEMAND ErrorControl = 1 ; SERVICE_ERROR_NORMAL ServiceBinary = %12%\Phaser.sys
The .Services section supports several different directives, but the most common is AddService. The AddService directive specifies the name of an AddService section, created by the .INF file author, which contains a series of directives that will create the necessary registry values to describe the driver. These values should be familiar to you if you’ve configured non-plug-and-play drivers “manually” via the registry.
“So,” I said, “that’s how you install a driver for a typical device on PCI, USB, and other enumerable buses.”
The little bear looked perplexed and asked, “But what about my Hunny filter? Someone else wrote the driver for the device that processes the Hunny, I just want to filter it.”
“That is easily handled by the following section,” I replied.
.HW
This section is primarily used to install PnP function filter drivers. After the .Services section has been run, the system device installer will search the .INF file for a section name that is identical to the DDinstall section, but with the suffix “.HW”. The .HW section is used to add entries to the “hardware” key (HKLM\System\CCS\Enum\<enumerator>\<device-id>\<instance-id>).
; ; Add the filter information to the registry ; [install-phaser.HW] AddReg = AddReg-HW ; ; Putting this in the registry causes the filter to be ; loaded ; [AddReg-HW] HKR,,"UpperFilters",0x00010000,"Hunny"
This section supports several different directives, but the most common is AddReg, which specifies a section (created by the .INF file writer) containing an entry to be added to the registry. Specify the key “UpperFilters” to load your driver as an upper filter (on top of the FDO), or “LowerFilters” to load your driver as a lower filter (below the FDO).
Any questions?
“O.K.,” I asked my budding .INF file writers, “any questions?”
They said that they all understood.
“Good. Now you get to apply a little of the knowledge you just gained. Following are a few questions you should all be able to answer,” I said.
After reading this tutorial, you should be able to answer the following questions:
- What section in an .INF file is used to install a hardware-specific function filter?
- What is the Manufacturer section used for?
- What types of devices must have their PnP Ids listed in the [ControlFlags] ExcludeFromSelect directive? Why?
- What is the Strings section used for?
What do you know! The furry little guys got all the answers correct, and they all ambled off into the woods to continue on their various projects. As the little pig walked away, he still regarded me with suspicion, as he watched his back.
“Say hello to Oscar Mayer for me!” I called back to him.
About the author:
Brian Catlin is a Windows driver consultant, trainer, and author based in Hawaii. He is co-founder of Azius Developer Training, which offers training seminars in a variety of subject areas, including driver programming, Windows internals, Windows security, and Windows forensics.