Programming the Microsoft Windows Driver Model

String Handling

WDM drivers can work with string data in any of four formats:

The UNICODE_STRING and ANSI_STRING data structures both have the layout depicted in Figure 3-13. The Buffer field of either structure points to a data area elsewhere in memory that contains the string data. MaximumLength gives the length of the buffer area, and Length provides the (current) length of the string without regard to any null terminator that might be present. Both length fields are in bytes, even for the UNICODE_STRING structure.

Figure 3-13. The UNICODE_STRING and ANSI_STRING structures.

The kernel defines three categories of functions for working with Unicode and ANSI strings. One category has names beginning with Rtl (for run-time library). Another category includes most of the functions that are in a standard C library for managing null-terminated strings. The third category includes the safe string functions from strsafe.h, which will hopefully be packaged in a DDK header named NtStrsafe.h by the time you read this. I can t add any value to the DDK documentation by repeating what it says about the RtlXxx functions. I have, however, distilled in Table 3-9 a list of now-deprecated standard C string functions and the recommended alternatives from NtStrsafe.h.

Table 3-9. Safe Functions for String Manipulation

Standard function (deprecated)

Safe UNICODE Alternative

Safe ANSI Alternative

strcpy, wcscpy, strncpy, wcsncpy

RtlStringCbCopyW, RtlStringCchCopyW

RtlStringCbCopyA, RtlString CchCopyA

strcat, wcscat, strncat, wcsncat

RtlStringCbCatW, RtlStringCchCatW

RtlStringCbCatA, RtlString CchCatA

sprintf, swprintf, _snprintf, _snwprintf

RtlStringCbPrintfW, RtlStringCchPrintfW

RtlStringCbPrintfA, RtlStringCchPrintfA

vsprintf, vswprintf, vsnprintf, _vsnwprintf

RtlStringCbVPrintfW, RtlStringCchVPrintfW

RtlStringCbVPrintfA, RtlStringCchVPrintfA

strlen, wcslen

RtlStringCbLengthW, RtlStringCchLengthW

RtlStringCbLengthA, RtlStringCchLengthA

NOTE

I based the contents of Table 3-9 on a description of how one of the kernel developers planned to craft NtStrsafe.h from an existing user-mode header named strsafe.h. Don t trust me trust the contents of the DDK!

It s also okay, but not idiomatic, to use memcpy, memmove, memcmp, and memset in a driver. Nonetheless, most driver programmers use these RtlXxx functions in preference:

You should use the safe string functions in preference to standard run-time routines such as strcpy and the like. As I mentioned at the outset of this chapter, the standard string functions are available, but they re often too hard to use safely. Consider these points in choosing which string functions you ll use in your driver:

Allocating and Releasing String Buffers

You often define UNICODE_STRING (or ANSI_STRING) structures as automatic variables or as parts of your own device extension. The string buffers to which these structures point usually occupy dynamically allocated memory, but you ll sometimes want to work with string constants too. Keeping track of who owns the memory to which a particular UNICODE_STRING or ANSI_STRING structure points can be a bit of a problem. Consider the following fragment of a function:

UNICODE_STRING foo; if (bArriving) RtlInitUnicodeString(&foo, "Hello, world!"); else { ANSI_STRING bar; RtlInitAnsiString(&bar, "Goodbye, cruel world!"); RtlAnsiStringToUnicodeString(&foo, &bar, TRUE); } RtlFreeUnicodeString(&foo); // <== don't do this!

In one case, we initialize foo.Length, foo.MaximumLength, and foo.Buffer to describe a wide character string constant in our driver. In another case, we ask the system (by means of the TRUE third argument to RtlAnsiStringToUnicodeString) to allocate memory for the Unicode translation of an ANSI string. In the first case, it s a mistake to call RtlFreeUnicodeString because it will unconditionally try to release a memory block that s part of our code or data. In the second case, it s mandatory to call RtlFreeUnicodeString eventually if we want to avoid a memory leak.

The moral of the preceding example is that you have to know where the memory comes from in any UNICODE_STRING structures you use so that you can release the memory only when necessary.

Категории