OSRLogo
OSRLogoOSRLogoOSRLogo x OSR Custom Development Services
OSRLogo
x

Everything Windows Driver Development

x
x
x
GoToHomePage xLoginx
 
 

    Mon, 11 Dec 2017     115613 members

   Login
   Join


 
 
Contents
  Online Dump Analyzer
OSR Dev Blog
The NT Insider
Downloads
ListServer / Forum
Driver Jobs
  Express Links
  · The NT Insider Digital Edition - May-June 2016 Now Available!
  · Windows 8.1 Update: VS Express Now Supported
  · HCK Client install on Windows N versions
  · There's a WDFSTRING?
  · When CAN You Call WdfIoQueueP...ously

Guest Article: Simplifying Development with DDK Macros

 

The DDK is a toolbox full of marvelous tools developers need for writing drivers.  Often overlooked, the DDK macros support a number of valuable capabilities for the programmer. Unlike the kernel calls, most of these macros are not described in the DDK documentation.  While using undocumented kernel calls can lead to trouble, macros are self-documenting since the source resides in the DDK include files.  The following is not an exhaustive list of all the macros the DDK provides, but these can make your development easier and more reliable.

 

Status Value Tests

Driver writers all know and use NT_SUCCESS even though it is not truly documented in the DDK.  Very few developers realize that NT_SUCCESS is one of a set of macros that tests the four severity levels of NTSTATUS.  All of these calls except NT_INFORMATION appear in the DDK examples. 

 

NT_SUCCESS            tests for a value with a severity of success or informational

NT_INFORMATION     tests for a severity value of informational

NT_WARNING            tests for warning severity

NT_ERROR                tests for an error level

 

For example, let’s look at NT_SUCCESS with the status from IoRegisterDeviceInterface:

 

status = IoRegisterDeviceInterface(pdo,InterfaceGuid,NULL,&symLink);

       if (NT_SUCCESS(status))

       {     

       // Now this driver is the provider for this interface– WRONG!

 

The above example could return an informational error, STATUS_OBJECT_NAME_EXISTS, and the assumptions of the code later on could be violated, if the driver depends on being the provider.  The code should be:

 

status = IoRegisterDeviceInterface(pdo,InterfaceGuid,NULL,&symLink);

       if (NT_SUCCESS(status) && !NT_INFORMATION(status))

       {     

       // Now this driver is the provider for this interface – WRONG!

 

Common “C” Macros

The DDK provides some common macros found in many “C” programming environments. For instance the DDK provides max(a,b) and min(a,b) for the developer.  A common code fragment seen in drivers:

 

if (b>a)

     {large = b;}

else

     {large = a;};

 

Where the following will do:

 

large = max(a,b);

 

Another common “C” macro returns the number of elements in a static array.  The DDK provides this as RTL_NUMBER_OF(A).  One of my favorite uses of this is to manipulate static lookup tables, for instance:

 

static PCHAR a[] = { “abc”, …,”xyz” }

 

for (i = 0; i <= RTL_NUMBER_OF(A); i++) 

// enumerate all the elements of A

 

In addition, if you need to do this for an element of a structure, the DDK supports RTL_NUMBER_OF_FIELD (type, field) that will return the number of elements of a field of the structure.

 

Structure Manipulation

Two macros that every developer should be aware of are CONTAINING_RECORD(address,type,field) and FIELD_OFFSET(type,field).  CONTAINING_RECORD takes the address of a field of a structure of a given type, the type and the name of the field, and returns a pointer to the beginning of the structure.  FIELD_OFFSET takes a structure type and a field name and returns the byte offset of the field.  For example:

 

typedef struct {

       FOO           bar;

       LIST_ENTRY    list;

       LAST          behind;

} BAR_LIST, *PBAR_LIST;

 

PLIST_ENTRY   l;

PBAR_LIST            p;

 

l = RemoveHeadList(set);

p = CONTAINING_RECORD(l,BAR_LIST,list);

 

The next two macros are extremely useful for variable size structures.  If you ever need to pass around a structure where not all fields are allocated, look into, RTL_ SIZEOF_THROUGH_FIELD(type, field) and RTL_ CONTAINS_FIELD(struct, size, Field).

 

typedef struct {

        COMMON     com;

        EXT1             extension1;

        EXT2             extension2;

} FOO, *PFOO;

 

PFOO          p;

 

// call to check extensions when only extension1 is present.

check_extensions(p, RTL_SIZEOF_THROUGH_FIELD(extension1)); 

 

void check_extension(PFOO p, ULONG size)

{

….

      If (RTL_CONTAINS_FIELD (FOO,size,extension1))

      [

          // perform checks on the first extension

 

Constant Strings

How many times have you used RtlInitUnicodeString, RtlInitString or RtlInitAnsiString with a constant string?  It turns out the DDK has a macro that can eliminate the call:

 

const static UNICODE_STRING strU = RTL_CONSTANT_STRING(L"Foo");

const static STRING str = RTL_CONSTANT_STRING( "Foo");

const static  ANSI_STRING strA = RTL_CONSTANT_STRING( "Foo");

 

Instead of:

 

UNICODE_STRING strU;

STRING str;

ANSI_STRING strA;

 

RtlInitUnicodeString(&FooU, L"Foo");

RtlInitString(&Foo ,  "Foo");

RtlInitAnsiString(&Foo ,  "Foo");

 

Data Alignment

Another common problem for developers is dealing with type alignment.  The DDK provides some simple macros to aid in this effort, ALIGN_DOWN_POINTER(address, type)  and ALIGN_UP_POINTER(address, type) will respectively round down and round up a pointer to the alignment of the type. For example you may need to allocate a buffer for a number of data structures where the last element is a string, but you want to guarantee the start of each structure is has a ULONG alignment:

 

Typedef struct {

    ULONG     len;

    CHAR      str[1];

} CNT_STR, *PCNT_STR;

 

PCNT_STR             p;

 

 

p->len = strlen(input);

strcpy(p->str,input);

p = ALIGN_UP_POINTER((PCHAR)p+p->len, ULONG);

 

The DDK also provides macros to round a length value up or down to a type alignment, via ALIGN_UP(length, type)  and ALIGN_DOWN(length, type).

 

Compile-time Asserts

Most developers are aware of the ASSERT macro, but how many use C_ASSERT?  This handy macro will perform compile time assertion checking in your code.  For example, the following will compile with an error:

 

#define LARGEST_BUFFER     100

#define SMALL_BUFFER 1000

 

C_ASSERT(SMALL_BUFFER < LARGEST_BUFFER);  

 

Parameter Macros

Another useful set of macros handle some simple parameter problems.  If you use /W4 to compile your code you have probably encountered warning messages complaining about unused arguments.  The UNREFERENCED_PARAMETER macro will take care of these warnings.  One of the documented macros is ARGUMENT_PRESENT.  While it simply tests for a null pointer, it helps clearly indicate the intent of the code.  For example:

 

VOID MyDpcRoutine(IN PKDPC Dpc,IN PVOID DeferredContext,

    IN PVOID SystemArgument1,IN PVOID SystemArgument2)

{

       UNREFERENCED_PARAMETER(Dpc);  // Dpc parameter is not used

 

       If (ARGUMENT_PRESENT(SystemArgument1))

       {

              // Perform work on SystemArgument1

 

 

Don Burn is a Windows system software architect, specializing in drivers and file systems, with over 25 years industry experience.  He's currently consulting while developing fault-tolerant technology for Windows.  Don can be reached at burn@acm.org.

 

Related Articles
WINVER Incorrectly Defined in XP/.NET Beta DDK's Win2K Build Environment
Advantage: Driver Writer -- New Functions in the Windows XP DDK
XP DDK Resets PATH Environment Variable
New DDK Package -- The DDK Suite (Update)
Need the XP DDK FAST?
Must Use New DDK Compiler
Windows XP® DDK
Interview: All About the DDK
The DDK Is Dead -- Long Live the LDK!
WHICH DDK Do I Use??

User Comments
Rate this article and give us feedback. Do you find anything missing? Share your opinion with the community!
Post Your Comment

"Macroses"
const static UNICODE_STRING strU = RTL_CONSTANT_STRING(L"Foo"); const static STRING str = RTL_CONSTANT_STRING( "Foo"); const static ANSI_STRING strA = RTL_CONSTANT_STRING( "Foo");

It's really easier than

RtlInitUnicodeString(&FooU, L"Foo"); RtlInitString(&Foo , "Foo"); RtlInitAnsiString(&Foo , "Foo");

thanx for this article

Rating:
13-Oct-08, Coderess Coderess


Post Your Comments.
Print this article.
Email this article.

Writing WDF Drivers I: Core Concepts
LAB

Nashua (Amherst), NH
15-19 May 2017

Writing WDF Drivers II: Advanced Implementation Techniques
LAB

Nashua (Amherst), NH
23-26 May 2017

Kernel Debugging and Crash Analysis
LAB

Dulles (Sterling), VA
26-30 Jun 2017

Windows Internals and Software Driver Development
LAB

Nashua (Amherst), NH
24-28 Jul 2017

 
 
 
 
x
LetUsHelp
 

Need to develop a Windows file system solution?

We've got a kit for that.

Need Windows internals or kernel driver expertise?

Bring us your most challenging project - we can help!

System hangs/crashes?

We've got a special diagnostic team that's standing by.

Visit the OSR Corporate Web site for more information about how OSR can help!

 
bottom nav links