oktava-studio.ru

Кириллица в native-приложении



Пишем на русском в native-режиме Windows




При разработке native-приложений может возникнуть необходимость писать на русском языке, но, как оказалось, добиться этого не так-то просто, так как по-умолчанию NtDisplayString не выводит кириллицу на экран. Для того, чтобы получить возможность вывода русских букв, нужно разобраться, где и в каком формате хранятся глифы символов, которые отображаются на экране. Если подробнее рассмотреть функцию winx_printf (проекте используется библиотека ZenWINX и заголовочные файлы NDK для упрощения разработки приложения), то мы увидим, что она в свою очередь вызывает winx_print, далее вызывается из ntdll.dll функция NtDisplayString, которая преобразует входящую строку с помощью RtlUnicodeStringToOemString, далее идёт вызов функции InbvDisplayString, которая обращается к VGA Boot Driver (bootvid.dll). Отображаемые глифы хранятся в bootvid.dll в следующем формате (на примере английской буквы A):

00000000 – 0×00
00000000 – 0×00
00011000 – 0×18
00011000 – 0×18
00100100 – 0×24
00100100 – 0×24
00100100 – 0×24
01111110 – 0×7E
01000010 – 0×42
10000001 – 0×81
00000000 – 0×00
00000000 – 0×00
00000000 – 0×00

Посмотреть остальные символы можно с помощью следующего нехитрого скрипта на Perl:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
open F, '
<p>
Таким образом, каждый символ имеет размер 8×13 пикселей и, соответственно, занимает 13 байт. Всего под символы отведено 256 * 13 = 3328 байт. То есть, чтобы добавить поддержку русского, необходимо найти начало таблицы глифов в памяти и заменить неиспользуемые символы своими глифами. Начало таблицы может меняться в зависимости от версии ОС, например, в Windows 7 смещение от начала составляет 0×2610, в Vista 0×2420, а в XP SP3 0×1938. Найти таблицу довольно просто, для этого достаточно найти в памяти первый глиф (0x00, 0x00, 0x3C, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x3C, 0x00, 0x00, 0x00).
</p>
<p>
Для начала необходимо составить свою таблицу глифов, чтобы заменить ею часть существующей таблицы. Вручную «рисовать» такое довольно муторно, поэтому следует поступить следующим образом: вывести в консоли windows список необходимых символов, сделал скриншот и преобразовать его в эдакий ASCII-арт.
Делается это следующим образом:
</p>
<pre class="brush: perl;">use GD;
my $im = GD::Image->newFromPng('image.png', 1) or die;
for(my $x = 1; $x width(); $x += 8)
{
 for(my $dy = 0; $dy getPixel($x + $dx, $dy);
   my ($r, undef, undef) = $im->rgb($index);
   if($r == 192)
   {
    print "1";
   }
   else
   {
    print "0";
   }
  }
  print "\n";
 }
 print "\n\n";
}
</pre>
<p>
И сразу же сворачиваем получившуюся таблицу в массив байт:
</p>
<pre class="brush: perl;">open F, ');
close F;
for(my ($i, $j) = (0, scalar @lines); $i
<p>
Конечно, последние два скрипта можно объединить в один, но так нагляднее. Также можно заметить, что в консоли выведен не только русский алфавит. Это связано с тем, что по-умолчанию русские буквы не располагаются непрерывно в шрифте (0x80 – 0xAF и 0xE0 – 0xF1), поэтому проще захватить весь интервал (0x80 – 0xF1).
</p>
<div align="center">
<a href="/img/native/rus-symbols.jpg">
<img src="/img/native/rus-symbols.jpg" width="245" alt="Русские символы">
</a><br><small>Русские символы</small>
</div>
<p>
Теперь, когда у нас есть готовая таблица, нам необходимо написать код, который найдёт и перезапишет необходимый фрагмент памяти.
Сначала мы должны найти bootvid.dll и адрес, по которому он загружен:
</p>
<pre class="brush: cpp;">NTSTATUS InitRussian()
{
 PRTL_PROCESS_MODULE_INFORMATION minfo = NULL;
 NTSTATUS code;
 ULONG i, m_size, glyph_offset = 0, image_size = 0;
 PVOID image = NULL;
 /* Размер, необходимый для RTL_PROCESS_MODULE_INFORMATION */
 code = NtQuerySystemInformation(SystemModuleInformation, minfo, 0, &m_size);
 if(code != STATUS_INFO_LENGTH_MISMATCH)
 {
  return code;
 }
 /* Выделяем память */
 code = AllocMemory((PVOID *)&minfo, m_size);
 if(!NT_SUCCESS(code))
 {
  return code;
 }
 /* Заполняем структуру */
 code = NtQuerySystemInformation(SystemModuleInformation, minfo, m_size, NULL);
 if(!NT_SUCCESS(code))
 {
  FreeMemory(minfo, m_size);
  return code;
 }
 /* Количество элементов в структуре */
 m_size = *(PULONG)minfo;
 minfo = (PRTL_PROCESS_MODULE_INFORMATION)((PUCHAR)minfo + 4);
 /* Перечисляем модули */
 for(i = 0; i
<p>
Также нам понадобятся дополнительные функции, с помощью которых мы будем читать и писать в память:
</p>
<pre class="brush: cpp;">NTSTATUS ReadVirtualMemory(PVOID VirtualAddress, PVOID Buffer, ULONG BufferSize)
{
 SYSDBG_VIRTUAL MemoryChunks;
 MemoryChunks.Address = VirtualAddress;
 MemoryChunks.Buffer = Buffer;
 MemoryChunks.Request = BufferSize;
 return NtSystemDebugControl(SysDbgReadVirtual, &MemoryChunks,
    sizeof(MemoryChunks), NULL, 0, NULL);
}
NTSTATUS WriteVirtualMemory(PVOID VirtualAddress, PVOID Buffer, ULONG BufferSize)
{
 SYSDBG_VIRTUAL MemoryChunks;
 MemoryChunks.Address = VirtualAddress;
 MemoryChunks.Buffer = Buffer;
 MemoryChunks.Request = BufferSize;
 return NtSystemDebugControl(SysDbgWriteVirtual, &MemoryChunks,
    sizeof(MemoryChunks), NULL, 0, NULL);
}
</pre>
<p>
Теперь прочитаем память bootvid и найдём начало таблицы:
</p>
<pre class="brush: cpp;">#define GLYPH_SIZE 13
char first_glyph[13] =
{0x00, 0x00, 0x3C, 0x24, 0x24, 0x24, 0x24,
 0x24, 0x24, 0x3C, 0x00, 0x00, 0x00};
</pre>
<pre class="brush: cpp;">/* Читаем содержимое памяти bootvid в буфер */
   code = ReadVirtualMemory(minfo[i].ImageBase, image, image_size);
   if(!NT_SUCCESS(code))
   {
    FreeMemory(minfo, m_size);
    FreeMemory(image, image_size);
    return code;
   }
   /* Ищем начало таблицы глифов */
   glyph_offset = search((char *)image, first_glyph, image_size, GLYPH_SIZE);
</pre>
<pre class="brush: cpp;">/* Функция поиска */
ULONG __stdcall search(char *x, char *y, unsigned int n, unsigned int m)
{
 unsigned int i;
 char first, second, *third;
 first = y[0];
 second = y[1];
 third = &y[2];
 for(i = 0; i
<p>
И, наконец, переписываем часть памяти:
</p>
<pre class="brush: cpp;">if (glyph_offset != 0)
   {
    /* Смещаемся на 128 глифов вперёд */
    glyph_offset += (128 * GLYPH_SIZE) + (ULONG)minfo[i].ImageBase;
    /* Записываем изменённую таблицу в память */
    return WriteVirtualMemory((PVOID)glyph_offset,
          ru_glyph, sizeof(ru_glyph));
   }
  }
 }
 /* Освобождаем память */
 FreeMemory(minfo, m_size);
 FreeMemory(image, image_size);
 return STATUS_NOT_FOUND;
}
</pre>
<p>
Таким образом, мы получили готовую функцию для добавления поддержки русского языка. Следующий код позволяет убедиться в том, что она отлично работает на XP SP3:
</p>
<pre class="brush: cpp;">void __stdcall NtProcessStartup(PPEB Argument)
{
 NTSTATUS code;
 char str[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n"
  "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя";
 zenwinx_native_init();
 winx_init(Argument);
 winx_printf("%s\n\n", str);
 winx_getch();
 code = InitRussian();
 if(code != STATUS_SUCCESS)
 {
  winx_printf("Error: 0x%x - %d\n", code, RtlNtStatusToDosError(code));
 }
 winx_printf("%s\n\n", str);
 winx_getch();
 winx_exit(0);
 return;
}
</pre>
<p>
А вот как выглядит результат работы:
</p>
<div align="center">
<a href="/img/native/native-mode-russian.jpg">
<img src="/img/native/native-mode-russian.jpg" width="320" alt="Русский язык в native-приложении">
</a><br><small>Русский язык в native-приложении</small>
</div>
<p>
Однако, у этого кода есть минус – он не работает под ОС выше XP SP3 и пока непонятно, как адаптировать его под них.
Исходный код проекта: <a href="/files/native-rus.zip">скачать</a>.
</p>
<br><br><div align="right"><p>Автор: kaimi.ru<br>Дата: 26.01.2011</p></div>
<br><div align="center">
</div>
</pre></pre></pre>





Copyright © 2016-2025 Программирование Native API и расширенные возможности NTFS
По вопросам сотрудничества и другим вопросам по работе сайта пишите на cleogroup[собака]yandex.ru