Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Ассемблер? Это просто! Учимся программировать


Информационный Канал Subscribe.Ru

Ассемблер под Windows #37.

Unicode в разных версиях Windows


Доброго времени суток, уважаемые подписчики. Сегодня мы поговорим о разных версиях Windows, и написании программ, использующих unicode, и работающих во всех актуальных версиях Windows (под Windows 3.1 наша программа работать не будет).


Наверно многие уже замечали, что драйвера и некоторые программы обычно имеют несколько версий для разных Windows. Причём среди всех 32-х битных Windows различают две группы, Win95/98/Me и WinNT/2k/XP. Отличий между этими группами Windows достаточно много, но сейчас нас интересует лишь одно отличие, поддержка (или отсутствие поддержки) unicode. Во времена появления Windows 95 поддержка одно единственного языка, отличного от английского уже считалась достаточно большим прорывом. О поддержке нескольких языков речи небыло. Достаточно логично, что вопросу использования unicode не было уделено достаточно внимания. Windows 98, а потом и Windows Millenium Edition, основой которых так и остался дос, появились как продолжение серии WIN32_WINDOWS, что и определило судьбу поддержки unicode в этих операционных системах - в них она так и не была включена. Отдельную ветвь операционных систем Windows представляют Windows NT, Windows 2000, Windows XP и Windows Server 2003, основанные на платформе WIN32_NT. В них для написания программы, работающей с unicode, достаточно лишь немного изменить (дописать W в имя функции) имена вызываемых функций. Но, как известно, спрос рождает предложение, возможность использовать unicode появилась и для Windows95/98/ME. Это стало возможно благодаря так называемому Microsoft Layer for Unicode. На самом деле это просто библиотека, содержащее большинство функций, предназначенных для работы с unicode. Не смотря на то что, на самом деле эта библиотека лишь проверяет наличие unicode-версии определённой функции и, в случае наличия такой функции, просто вызывает её, а в противном случае конвертирует строку в ANSI и вызывает ANSI-версию определённой функции, эта библиотека даёт возможность писать идентичный исполняемый код и для Win95/98/Me и для WinNT/2k/XP. Выходит, что для того чтоб программу можно было запускать во всех версиях Win32, нужно импортировать функции не из kernel32.dll, user32.dll и т.д., а из unicows.dll (это и есть библиотека Microsoft Layer for Unicode). Этот способ будет работать, но мне кажется достаточно нелогичным то, что работая в WinNT/2k/XP мы используем 250-и килобайтную библиотеку, которая лишь вызывает нужную нам функцию, но при этом создавая стековой кадр, проверяя версию Windows, повторно помещая параметры в стек и т.д. Конечно, сегодняшние быстрые процессоры с лёгкостью справятся с лишней сотен команд, но думаю не будет лишним рассмотреть возможные разрешения этой нелогичности. Во-первых стоит динамически загружать библиотеки и функции, это даст возможность загружать функции из разных библиотек в зависимости от версии операционной системы. Во-вторых следует отказаться от использования библиотеки unicows.dll, а написать подобные функции самим (зная, что на самом деле библиотека лишь конвертирует строку в ANSI и вызывает ANSI-версию нужной функции, это достаточно ожидаемый шаг). Однако если цель не минимальный размер программы, то имеет смысл использовать лишь динамическое импортирование функций. Рассмотрим пример такой программы, это конвертатор строк из прошлого урока:
include kernel32.inc
include user32.inc
include C:\Assembly\def32.inc
include C:\Assembly\macros.inc
include C:\Assembly\ole32.inc
  .386
  .model flat
  option casemap:none

_DATA SEGMENT public USE32 'data'
wUniASCII dw 41Ah,43Eh,43Dh,432h,435h,440h,442h,430h,442h,43Eh,440h,20h,55h,6Eh,69h,63h
          dw 6Fh,64h,65h,20h,441h,442h,440h,43Eh,43Ah,0h
wUniStr   dw 'U','n','i','S','t','r',0
wEdit     dw 'E','d','i','t',0
wButton   dw 'B','u','t','t','o','n',0
wConvert  dw 41Ah,43Eh,43Dh,432h,435h,440h,442h,438h,440h,43Eh,432h,430h,442h,44Ch,0h

aUser32_dll       db 'user32.dll',0
aKernel32_dll     db 'kernel32.dll',0
aUnicows_dll      db 'unicows.dll',0
aGetModuleHandleW db 'GetModuleHandleW',0
aCreateWindowExW  db 'CreateWindowExW',0
aGetMessageW      db 'GetMessageW',0
aDispatchMessageW db 'DispatchMessageW',0
aLoadIconW        db 'LoadIconW',0
aLoadCursorW      db 'LoadCursorW',0
aRegisterClassExW db 'RegisterClassExW',0
aDefWindowProcW   db 'DefWindowProcW',0
aGetDlgItemTextW  db 'GetDlgItemTextW',0

a_lu db '%lXh',0
_DATA ENDS

_BSS SEGMENT public USE32 'bss'
hInstance dd ?
wBuffer   dd ?
aBuffer   dd ?
Counter   dd ?

GetModuleHandleW dd ?
CreateWindowExW  dd ?
GetMessageW      dd ?
DispatchMessageW dd ?
LoadIconW        dd ?
LoadCursorW      dd ?
RegisterClassExW dd ?
DefWindowProcW   dd ?
GetDlgItemTextW  dd ?

OSVersion OSVERSIONINFOA<>
_BSS ENDS

_TEXT SEGMENT public USE32 'code'
_start:
;Для разнообразия пусть структура сообщения будет находиться не в статической памяти
  enter sizeof(MSG), 00h
  null ebx
  run InitProgram ;Инициализируем используемые функции, см. ниже
  jeaxz EndProgram
  run GetModuleHandleW, ebx
  mov hInstance, eax
  run RigisterMyClass
  jeaxz EndProgram
  mov ecx,CW_USEDEFAULT
  run CreateWindowExW, ebx, offset wUniStr, offset wUniASCII, WS_BORDER or WS_CAPTION or WS_SYSMENU or WS_MINIMIZE or WS_VISIBLE, ecx, ecx, 250, 150, ebx, ebx, hInstance, ebx
  jeaxz EndProgram
  push eax
  run ShowWindow, eax, SW_SHOWNORMAL
  run UpdateWindow
  lea edi,[ebp-sizeof(MSG)]
  run GetMessageW, edi, ebx, ebx, ebx
MainLoop:
  run TranslateMessage, edi
  run DispatchMessageW, edi
  run GetMessageW, edi, ebx, ebx, ebx
  jeaxnz MainLoop
EndProgram:
  leave
  run ExitProcess, ebx

RigisterMyClass proc
  enter sizeof(WNDCLASSEX), 00h
  lea edi,[ebp-sizeof(WNDCLASSEX)]
  assume edi:PTR WNDCLASSEX
  mov [edi].cbSize,  sizeof(WNDCLASSEX)
  mov [edi].style,  CS_VREDRAW or CS_HREDRAW
  mov [edi].lpfnWndProc,  offset WndProc
  mov [edi].cbClsExtra,  ebx
  mov [edi].cbWndExtra,  ebx
  mov [edi].hInstance,  eax
  run LoadIconW, ebx, IDI_APPLICATION
  mov [edi].hIcon,  eax
  run LoadCursorW, ebx, IDC_ARROW
  mov [edi].hCursor,  eax
  mov [edi].hbrBackground,COLOR_WINDOW
  mov [edi].lpszMenuName,  ebx
  mov [edi].lpszClassName,offset wUniStr
  mov [edi].hIconSm,  ebx
  run RegisterClassExW, edi
  assume edi:nothing
  leave
  ret
RigisterMyClass endp

WndProc proc
  enter 0, 0
  pusha
  mov edi,[ebp+08h]
  null ebx
  mov eax,[ebp+0Ch]
  cmp eax,WM_COMMAND
  je command_proc
  cmp eax,WM_CREATE
  je create_proc
  cmp eax,WM_CLOSE
  je close_proc
  cmp eax,WM_DESTROY
  je destroy_proc
  popa
  leave
  jmp DefWindowProcW
create_proc:
  run CreateWindowExW, ebx, offset wEdit, ebx, WS_CHILD or WS_BORDER or WS_VISIBLE or ES_AUTOHSCROLL, 15, 25, 220, 22, edi, 100, hInstance, ebx
  run CreateWindowExW, ebx, offset wEdit, ebx, WS_CHILD or WS_BORDER or WS_VISIBLE or ES_AUTOVSCROLL, 15, 55, 220, 22, edi, 101, hInstance, ebx
  run CreateWindowExW, ebx, offset wButton, offset wConvert, WS_CHILD or WS_BORDER or WS_VISIBLE, 45, 85, 160, 26, edi, 102, hInstance, ebx
;Создаём два буфера, размером в 4096 символов
  run CoTaskMemAlloc, 12288
  mov wBuffer, eax
  add eax, 8192
  mov aBuffer, eax
EndWndProc:
  popa
  leave
  null eax
  ret 16
close_proc:
  run CoTaskMemFree, wBuffer
  run DestroyWindow, edi
  jmp EndWndProc
destroy_proc:
  run PostQuitMessage, ebx
  jmp EndWndProc
command_proc:
  cmp word ptr [ebp+10h], 102
  jne EndWndProc
;Здесь происходит посимвольный перевод unicode строки в ассемблерную
  mov esi, wBuffer
  mov dword ptr [esi+8192],ebx
  run GetDlgItemTextW, edi, 100, esi, 4096
  inc eax
  mov Counter, eax
loop_convert:
  mov ecx,aBuffer
  run CheckStringLengthA, ecx
  add eax,ecx
  movzx ecx,word ptr [esi+ebx*2]
  run wsprintfA, eax, offset a_lu, ecx
  add esp, 12
  inc ebx
  cmp ebx, Counter
  jz end_loop_convert
  mov ecx,aBuffer
  run CheckStringLengthA, ecx
  mov word ptr [eax+ecx],','
  jmp loop_convert
end_loop_convert:
  run SetDlgItemText, edi, 101, aBuffer
  jmp EndWndProc
WndProc endp

;Обратите внимание на эту функцию, именно эта функция импортирует адреса функций.
;В зависимости от платформы операционной системы функция заносит в esi handle нужной
;библиотеки и вызывает функции получения адресов процедур.

InitProgram proc
  mov OSVersion.dwOSVersionInfoSize, sizeof(OSVERSIONINFOA)
  run GetVersionExA, offset OSVersion
  cmp OSVersion.dwPlatformId, VER_PLATFORM_WIN32_NT
  je init_nt
  cmp OSVersion.dwPlatformId, VER_PLATFORM_WIN32_WINDOWS
  je init_win9x
init_failed:
  null eax
  ret
init_nt:
  run LoadLibraryA, offset aUser32_dll
  jeaxz init_failed
  mov esi,eax
  run GetUserFunctions
  jeaxz init_failed
  run LoadLibraryA, offset aKernel32_dll
  jeaxz init_failed
  mov esi,eax
  run GetKernelFunctions
  jeaxz init_failed
  null eax
  inc eax
  ret
init_win9x:
  run LoadLibraryA, offset aUnicows_dll
  jeaxz init_failed
  mov esi,eax
  run GetUserFunctions
  jeaxz init_failed
  run GetKernelFunctions
  jeaxz init_failed
  null eax
  inc eax
  ret
InitProgram endp

GetKernelFunctions proc
  run GetProcAddress, esi, offset aGetModuleHandleW
  jeaxz gkf_failed
  mov GetModuleHandleW, eax
  null eax
  inc eax
  ret
gkf_failed:
  null eax
  ret
GetKernelFunctions endp

GetUserFunctions proc
  run GetProcAddress, esi, offset aCreateWindowExW
  jeaxz gkf_failed
  mov CreateWindowExW, eax
  run GetProcAddress, esi, offset aGetMessageW
  jeaxz gkf_failed
  mov GetMessageW, eax
  run GetProcAddress, esi, offset aDispatchMessageW
  jeaxz gkf_failed
  mov DispatchMessageW, eax
  run GetProcAddress, esi, offset aLoadIconW
  jeaxz gkf_failed
  mov LoadIconW, eax
  run GetProcAddress, esi, offset aLoadCursorW
  jeaxz gkf_failed
  mov LoadCursorW, eax
  run GetProcAddress, esi, offset aRegisterClassExW
  jeaxz gkf_failed
  mov RegisterClassExW, eax
  run GetProcAddress, esi, offset aDefWindowProcW
  jeaxz gkf_failed
  mov DefWindowProcW, eax
  run GetProcAddress, esi, offset aGetDlgItemTextW
  jeaxz gkf_failed
  mov GetDlgItemTextW, eax
  null eax
  inc eax
  ret
gkf_failed:
  null eax
  ret
GetUserFunctions endp

CheckStringLengthA proc
  mov eax,[esp+04h]
  dec eax
LoopCSL:
  inc eax
  cmp byte ptr [eax],0
  jnz LoopCSL
  sub eax,[esp+04h]
  ret 4
CheckStringLengthA endp

  end _start
_TEXT ENDS
Для определения платформы Windows используется функция GetVersionExA, которой передаётся указатель на структуру OSVERSIONINFOA
OSVERSIONINFOA STRUCT
  dwOSVersionInfoSize   dd ?
  dwMajorVersion        dd ?
  dwMinorVersion        dd ?
  dwBuildNumber         dd ?
  dwPlatformId          dd ?
  szCSDVersion          db 128 dup(?)
OSVERSIONINFOA ENDS
Думаю, этого достаточно для написание приложения, работающего с unicode, и советую всем, для практики, написать хотя-бы наипростейшую программу, выводящую unicode-MessageBox...


На сегодня это всё, если есть какие вопросы, пишите. Пишите на Dark_Lord@RusFAQ.ru или свяжитесь со мной по ICQ (269764648), MSN (Dark_Lord@RusFAQ.ru) или Yahoo (durklard). Предыдущие выпуски можно найти на сайте рассылки



Сайт рассылки LearnAsm.narod.ru
При поддержке RusFAQ.ru


http://subscribe.ru/
http://subscribe.ru/feedback/
Подписан адрес:
Код этой рассылки: comp.prog.assembler
Отписаться

В избранное