Операции над строками UNICODE_STRING
Операционная система Windows NT издавна ориентирована на использование так называемых "широких" символов (занимающих два байта), что, в отличие от символов ASCII, o которых будет сказано ниже, без особых затруднений обеспечивает поддержку всех типов алфавитов, включая поддержку языков Юго-Восточной Азии.
Собственно тип данных UNICODE_STRING описывается в пакете DDK следующим образом (см. заголовочный файл ntdef.h):
typedef struct _UNICODE_STRING { USHORT Length; // Длина строки (в двухбайтных символах) USHORT MaximumLength; // Максимально возможная длина строки PWSTR Buffer; // указатель на буфер с двухбайтными символами } UNICODE_STRING, *PUNICODE_STRING;
Иногда UNICODE_STRING определяется выражением "counted string", что довольно точно передает сущность этого типа данных, то есть — "счетная строка", строка, где поддерживается учет действующих символов.
Нетрудно догадаться, что у программистов, долгое время привыкавших к простым ASCII кодировкам и столь же несложным функциям, оперирующим ASCIIZ строками, переход к использованию кодировки Юникод не вызовет энтузиазма.
Тем не менее, при работе в режиме ядра Windows NT это совершенно необходимый инструмент. (Правда, функции отладочной диагностики типа DbgPrint
позволяют пользоваться строками в прежней манере.)
Работа с типом UNICODE_STRING покажется менее сложной, если смириться с тем, что не следует "трогать его руками" (примерно, как CString в MFC), и научиться использовать набор системных функций, предназначенный для работы с ним.
Прежде всего, простая буква 'L', примененная перед строкой символов, дает указание препроцессору, трактовать эту строку как строку "широких" символов (строку WCHAR, но еще — не UNICODE_STRING). Соответственно, перейти от обычного текста, набираемого на клавиатуре компьютера, к строке UNICODE_STRING можно так:
UNICODE_STRING myNewUString; RtlInitUnicodeString( &myNewUString, L"My Unicode text example." );
В следующем примере посредником при инициализации UNICODE_STRING выступает тип данных ANSI_STRING (счетная строка однобайтных символов):
ANSI_STRING myNewANSIString; UNICODE_STRING myNewUString; RtlInitAnsiString( &myNewUString, "My second text example." ); RtlAnsiStringToUnicodeString(&myNewUString, &myNewANSIString, TRUE);
Третий параметр, указанный как TRUE, заставляет данную функцию выделять память под буфер двухбайтных символов. Соответственно, по окончании работы с UNICODE_STRING (а в данном случае — при выходе из текущей функции, поскольку myNewUString определена как локальная переменная) следует выполнить освобождение буфера двухбайтных символов вызовом RtlFreeUnicodeString. To же необходимо проделать и в первом примере. Более того, аналогичное требование и к типу данных ANSI_STRING, для которого следует использовать RtlFreeAnsiString.
Сам тип ANSI_STRING определяется следующим образом (см. ntdef.h):
typedef struct _STRING { USHORT Length; USHORT MaximumLength; PCHAR Buffer; // Здесь 'PCHAR' - просто 'char' указатель } STRING, *PSTRING; typedef STRING ANSI_STRING;
Очевидно, второй способ более трудоемкий, и им редко кто пользуется.
После того как экземпляр UNICODE_STRING получен, над ним можно выполнять разнообразные операции. Предназначенные для этого системные функции описываются ниже.
Таблица 7.33. Прототип вызова RtlAppendUnicodeStringToString
NTSTATUS RtlAppendUnicodeStringToString | IRQL — любой (если это допускает тип памяти буферов двухбайтных символов) |
Параметры | Объединяет строки UNICODE_STRING |
IN OUT PUNICODE_STRING Destination | Указатель на строку-получатель |
IN OUT PUNICODE_STRING AppendString | Указатель на присоединяемую строку |
Возвращаемое значение | STATUS_SUCCESS — строка присоединена и длина строки получателя обновлена STATUS_BUFFER_TOO_SMALL — слишком мал размер буфера двухбайтных символов строки-получателя |
Если требуемый размер буфера не составит труда вычислить как
(Destination->Length + AppendString->Length) * sizeof(WCHAR)
то расширение буфера строки-получателя — задача несколько более сложная. Хотя логично было бы иметь соответствующую системную функцию, однако в документации DDK o подобном вызове нет никакого упоминания.
Таблица 7.34. Прототип вызова RtlCompareUnicodeString
LONG RtlCompareUnicodeString | IRQL == PASSIVE_LEVEL |
Параметры | Выполняет сравнение строк UNICODE_STRING |
IN PUNICODE_STRING pString1 | Указатель на первую строку |
IN PUNICODE_STRING pString2 | Указатель на вторую строку |
BOOLEAN CaseInSensitive | TRUE — игнорировать регистр (верхний/нижний) |
Возвращаемое значение | 0 — строки идентичны < 0 — строка 1 меньше строки 2 |
BOOLEAN RtlEqualUnicodeString | IRQL == PASSIVE_LEVEL |
Параметры | Выполняет сравнение строк UNICODE_STRING |
IN PUNICODE_STRING pString1 | Указатель на первую строку |
IN PUNICODE_STRING pString2 | Указатель на вторую строку |
BOOLEAN CaseInSensitive | TRUE — игнорировать регистр (верхний/нижний) |
Возвращаемое значение | TRUE — строки идентичны FALSE — строки различаются |
NTSTATUS RtlInt64ToUnicodeString | IRQL == PASSIVE_LEVEL |
Параметры | Преобразует число int64 в UNICODE_STRING |
IN ULONGLONG Value | Исходное число |
IN ULONG Base | Формат: 16 — шестнадцатеричный, 8 — октавный, 2 — двоичный, 0 или 10 — десятичный. |
IN OUT PUNICODE_STRING s | Строка UNICODE_STRING |
Возвращаемое значение |
STATUS_SUCCESS STATUS_BUFFER_OVERFLOW — слишком мал размер буфера UNICODE_STRING STATUS_INVALID_PARAMETER — ошибочен параметр Base |
и RtlIntPtrToUnicodeString, соответственно.
Таблица 7.37. Прототип вызова RtlUpcaseUnicodeString
NTSTATUS RtlUpcaseUnicodeString | IRQL == PASSIVE_LEVEL |
Параметры | Преобразует все символы строки src в символы верхнего регистра |
IN OUT OPTIONAL PUNICODE_STRING dest | Указатель на строку с буфером, подготовленным для приема преобразованной строки или NULL (в последнем случае преобразование происходит по месту) |
IN OUT PUNICODE_STRING src | Исходная строка |
IN BOOLEAN AllocateDstStringBuff | Если TRUE — выделить буфер под результат преобразования (в этом случае его следует освободить вызовом RtlFreeUnicodeString по окончании работы с этой строкой) |
Возвращаемое значение |
STATUS_SUCCESS STATUS_BUFFER_OVERFLOW — слишком мал размер буфера UNICODE_STRING STATUS_INVALID_PARAMETER — ошибочен параметр Base |