Пользователям гораздо удобней работать с программой на своём родном языке, а потому поддержка многоязычного интерфейса "Multilingual User Interface" является сейчас одним из обязательных требований заказчиков к программистам. На рынке предлагается множество готовых решений, но все они имеют следующие недостатки:
Поэтому в данной статье мы рассмотрим парочку простых методов создания MUI-интерфейса штатными средствами WinAPI. Для описания строк в окне, первая технология использует специально предназначенные для этих целей внешние текстовые файлы с расширением *.LNG, а вторая подразумевает создание одинаковых ресурсов в РЕ-файле, только на разных языках, которые загружаются вручную по выбору из меню, или автоматически, согласно предпочтительного языка в системе "Preferred UI Languages".
Оглавление:
1. Теоретические основы
Как правило, установщики Windows поставляются на каком-то одном языке, например русская версия, английская, китайская, и т.д. От этого выбора зависит, на каком языке будет в дальнейшем отображаться весь оконный интерфейс системы, т.е. текст в заголовках, на кнопках, в различного рода сообщениях, и многое другое. Однако это вовсе не означает, что установив английскую версию Win мы будем вынуждены теперь пройти курсы английского, чтобы общаться с ОС на предлагаемом ею языке.
Дело в том, что начиная Win2k система официально стала много-язычной, в отличии от Win3.1, которая считалась моно с поддержкой исключительно Eng. Теперь в каталоге system32 можно обнаружить несколько папок с именами разных локалей типа en-EN, ru-RU и т.д - это и есть языковые пакеты, на которые мы можем переключиться в любой момент уже после установки англо-язычной версии ОС. На своей Win7 я насчитал всего 36 таких папок, и в каждой из них имеются свои системные либы comdlg32.dll + comctl32.dll (dialog и controls соответственно) для разных языков.
Сменить интерфейс системы можно в оснастке "Язык и региональные стандарты" панели управления. Обратите внимание, что раскладка клавиатуры и язык интерфейса это разные вещи, а потому не нужно их путать. Если среди уже предустановленных языковых пакетов не окажется нужно вам языка, система предложит скачать его с сервера Microsoft.
1.1. Определение системной локали
Для кодирования языков система использует идентификаторы LCID или "Locale Identifier", которые строго определены международным стандартом NLS "National Language Standart". Размер LCID равен 32-битный дворд, при чём старшие 12-бит лежат в резерве так, что в младших 16-ти можно закодировать аж 65.536 различных наборов летописи, хотя реально в мире существует намного меньше языков.
Поэтому непосредственно язык LANGUAGE кодируется в младших 10 битах LCID = 1024 различных вариантов, а старшие 6 бит выделяются для точного определения синтаксиса внутри конкретного языка SUBLANG - это позволяет уточнить ещё 64 типа. Более того имеются доп.4 бита для указания приоритета сортировки SORT-ID на случай, когда нужно выбрать одного из двух одинаковых зайцев. Поле используется редко, а потому можно им принибречь.
Среди инклудов fasm'a имеется ..\equates\kernel32.inc, где среди прочего можно найти определение констант LCID. Обратите внимание на значения из списка SUBLANG - для их кодирования используется инструкция сдвига влево на 10-бит
Все локали в системе собираются в свои группы, например Западная Европа, далее Кириллица, Китайская, Греческая, Прибалтика, и т.д. Из покон веков среди WinAPI существовала функция
Но проблема в том, что функи эти дампят информацию без фильтра реально поддерживаемых в текущей системе, т.е. просто все возможные варианты в природе. Поэтому если мы хотим получить фактический лист, нужно прочитав строку LCID проверить её наличие в папке system32 через
Так получим присутствующие в нашей системе языковые пакеты из \system32, которых лично у меня оказалось всего 36 штук. Здесь ID определяет группу (будет одинакова во всех осках):
1.2. Предпочтительный язык интерфейса
Одним из компонентов загрузчика РЕ-файлов является лоадер ресурсов. Он оперирует такими api как
На этапе отображения окна
Но что примечательно, ОС предлагает нам не только запросить этот список, но и функцией
2. Пример многоязычной программы с разными ресурсами
Теперь проверим этот тезис на практике.
Значит алгоритм программы должен выглядеть примерно так:
Теперь поговорим о нюансах.
Суть в том, что мы определяем по 2 ресурса на разных языках LCID, но обязательно с одинаковым ID самих ресурсов. Вот пример из моей демки:
Здесь видно, что ID обоих ресурсов "Menu" равен 37, а вот локали LCID будут у них уже разные - для LANG_RUSSIAN получим
Чтобы убедиться в правильном оформлении ресурсов, можно вскормить прожку вьюверу "CFF-Explorer". И точно.. видим 2 дира с
Как результат, в момент запуска нашего приложения загрузчик ресурсов запросит у системы предпочтительный язык LCID, найдёт его в нашей секции-ресурсов, и магическим образом сам загрузит нужное окно. Всё что требуется от нас - это при смене языка в меню, тут-же подменить и предпочтительный LCID. Кстати вот идентификаторы каталогов в ресурсах, которые определены на глобальном уровне всех операционных систем Windows (см.разделы AKA выше).
А так должно выглядеть описание ресурсов на разных языках:
3. Пример многоязычной программы с внешними файлами
Предложенный выше вариант многоязычной программы имеет 1 основной недостаток - для добавления новых языков требуется полная перекомпиляция исходного кода. Чтобы решить эту проблему, можно создать единственное дефолтное окно, а при запросе на смену языка, читать текстовые строки из заранее подготовленных внешних файлов. Тогда, чтобы добавить в программу новый язык, достаточно будет пользователю самому создать обычный файл с расширением LNG, в котором будет храниться пара
Если приглядеться, то формат файлов LNG является точной ксерокопией файлов инициализации INI, поэтому всё сказанное ниже можно применить и в контексте создания файлов конфига CFG, т.к. вся эта тройка одного поля ягоды. Кстати данная техника находит широкое применение во всей программной области, например в механизмах подключения плагинов к уже готовому софту лишь с тем отличием, что вместо txt здесь используют dll.
Раз уж сами майки предлагают нам форматы файлов
Единственная проблема - функцию нужно вызывать для каждого ключа в файле, т.к. за один выстрел она возвращает только одно значение строки по указанному ключу, которое нужно будет сразу отправить в элемент окна через
Вот алгоритм, что нам придётся реализовать:
Не смотря на то, что данный метод реализации MUI-интерфейса немного сложнее предыдущего, он позволяет малой кровью создать приложение на абсолютно любом языке мира - просто поместите рядом с EXE свой файл LNG, и опишите в нём соответствующие строки. При этом нужно будет строго придерживаться дефолтного формата.
В частности, значение ключа "Name" должно обязательно совпадать с именем LNG-файла, т.к. динамическое создание пунктов меню и открытие файлов для чтения используют одно и то-же имя. Ради прикола попробуйте создать файл со-строками от фонаря типа
4. Выводы и рекомендации
Создать в своей программе многоязычный интерфейс достаточно просто, и теперь дело за малым - придумать такой софт, чтобы он был интересен пользователям во всём мире. Тогда его начнут переводить на различные языки, что существенно повысит планку дружелюбности по отношению к юзерам. В скрепке найдёте три исходника на ассемблере fasm представленных здесь демок, а так-же уже скомпилированные их бинари для тестов. Всем удачи, пока!
1. Обычно платные
2. Приводят в существенному увеличению размера софта
3. Являются сложными для понимания
4. Строки хранятся внутри уже скомпилированных DLL, что затрудняет внесение в них правок.
Поэтому в данной статье мы рассмотрим парочку простых методов создания MUI-интерфейса штатными средствами WinAPI. Для описания строк в окне, первая технология использует специально предназначенные для этих целей внешние текстовые файлы с расширением *.LNG, а вторая подразумевает создание одинаковых ресурсов в РЕ-файле, только на разных языках, которые загружаются вручную по выбору из меню, или автоматически, согласно предпочтительного языка в системе "Preferred UI Languages".
Оглавление:
1. Теоретические основы
2. Пример многоязычной программы с разными ресурсами
- устройство и разбор секции ресурсов
3. Пример многоязычной программы с внешними файлами
- системная поддержка файлов ini/lng
4. Выводы и рекомендации
1. Теоретические основы
Как правило, установщики Windows поставляются на каком-то одном языке, например русская версия, английская, китайская, и т.д. От этого выбора зависит, на каком языке будет в дальнейшем отображаться весь оконный интерфейс системы, т.е. текст в заголовках, на кнопках, в различного рода сообщениях, и многое другое. Однако это вовсе не означает, что установив английскую версию Win мы будем вынуждены теперь пройти курсы английского, чтобы общаться с ОС на предлагаемом ею языке.
Дело в том, что начиная Win2k система официально стала много-язычной, в отличии от Win3.1, которая считалась моно с поддержкой исключительно Eng. Теперь в каталоге system32 можно обнаружить несколько папок с именами разных локалей типа en-EN, ru-RU и т.д - это и есть языковые пакеты, на которые мы можем переключиться в любой момент уже после установки англо-язычной версии ОС. На своей Win7 я насчитал всего 36 таких папок, и в каждой из них имеются свои системные либы comdlg32.dll + comctl32.dll (dialog и controls соответственно) для разных языков.
Сменить интерфейс системы можно в оснастке "Язык и региональные стандарты" панели управления. Обратите внимание, что раскладка клавиатуры и язык интерфейса это разные вещи, а потому не нужно их путать. Если среди уже предустановленных языковых пакетов не окажется нужно вам языка, система предложит скачать его с сервера Microsoft.
1.1. Определение системной локали
Для кодирования языков система использует идентификаторы LCID или "Locale Identifier", которые строго определены международным стандартом NLS "National Language Standart". Размер LCID равен 32-битный дворд, при чём старшие 12-бит лежат в резерве так, что в младших 16-ти можно закодировать аж 65.536 различных наборов летописи, хотя реально в мире существует намного меньше языков.
Поэтому непосредственно язык LANGUAGE кодируется в младших 10 битах LCID = 1024 различных вариантов, а старшие 6 бит выделяются для точного определения синтаксиса внутри конкретного языка SUBLANG - это позволяет уточнить ещё 64 типа. Более того имеются доп.4 бита для указания приоритета сортировки SORT-ID на случай, когда нужно выбрать одного из двух одинаковых зайцев. Поле используется редко, а потому можно им принибречь.
Среди инклудов fasm'a имеется ..\equates\kernel32.inc, где среди прочего можно найти определение констант LCID. Обратите внимание на значения из списка SUBLANG - для их кодирования используется инструкция сдвига влево на 10-бит
SHL, что как-раз представлено на рис.выше. То-есть он попадает в зелёную область. Таким образом, для русской локали получаем LCID=0x0419, а для дефолтной американо-английской LCID=0x0409. Если в LCID задействовано поле SORT_ID, его значение будет больше 2-х байт, например 0x010419.
C-подобный:
;// Language identifiers
LANG_CHINESE = 04h
LANG_ENGLISH = 09h
LANG_RUSSIAN = 19h
........
;// Sublanguage identifiers
SUBLANG_CHINESE_TRADITIONAL = 01h shl 10
SUBLANG_CHINESE_SIMPLIFIED = 02h shl 10
SUBLANG_CHINESE_HONGKONG = 03h shl 10
SUBLANG_CHINESE_SINGAPORE = 04h shl 10
SUBLANG_ENGLISH_US = 01h shl 10
SUBLANG_ENGLISH_UK = 02h shl 10
SUBLANG_ENGLISH_AUS = 03h shl 10
SUBLANG_ENGLISH_CAN = 04h shl 10
SUBLANG_ENGLISH_NZ = 05h shl 10
SUBLANG_ENGLISH_EIRE = 06h shl 10
SUBLANG_RUSSIAN_RUSSIA = 01h shl 10
.....
Все локали в системе собираются в свои группы, например Западная Европа, далее Кириллица, Китайская, Греческая, Прибалтика, и т.д. Из покон веков среди WinAPI существовала функция
EnumSystemLanguageGroups(), которая возвращает полный их список. Родственная ей EnumLanguageGroupLocales() перечисляет уже все локали внутри группы.Но проблема в том, что функи эти дампят информацию без фильтра реально поддерживаемых в текущей системе, т.е. просто все возможные варианты в природе. Поэтому если мы хотим получить фактический лист, нужно прочитав строку LCID проверить её наличие в папке system32 через
FindFirstFile(), хотя можно просто запросить атрибуты папки по имени GetFileAttributes(). Если последняя вернёт ошибку EAX=-1, значит прокол. Вот пример такого парсера и результат его работы:
C-подобный:
format pe64 console
include 'win64ax.inc'
entry start
;//-------------
section '.data' data readable writeable
fPath rb 64
buff db 0
;//-------------
section '.text' code readable executable
start: sub rsp,8
invoke EnumSystemLanguageGroups,EnumGroup,1,0 ;// 1 = LGRPID_INSTALLED
cinvoke _getch
cinvoke exit,0
align 8
proc EnumGroup id, groupStr
mov [id],rcx
mov [groupStr],r8
invoke CharToOem,r8,buff
cinvoke printf,<10,10,' ID-%02d %s',0>,[id],buff
invoke EnumLanguageGroupLocales,EnumLocales,[id],0,0
mov eax,1
ret
endp
align 8
proc EnumLocales lcid
mov [lcid],rdx
invoke LCIDToLocaleName,rdx,buff,16,0
mov rsi,buff
mov rdi,rsi
mov ecx,12
@@: lodsw ;//<--- UnicodeToAnsi
stosb
loop @b
cinvoke wsprintf,fPath,<'C:\Windows\system32\%s',0>,buff
invoke GetFileAttributes,fPath
or eax,eax
js @f
cinvoke printf,<10,' 0x%04x = %s',0>,[lcid],buff
@@: mov eax,1
ret
endp
;//-------------
section '.idata' import data readable writeable
library msvcrt,'msvcrt.dll',kernel32,'kernel32.dll',user32,'user32.dll'
include 'api\kernel32.inc'
include 'api\user32.inc'
include 'api\msvcrt.inc'
Так получим присутствующие в нашей системе языковые пакеты из \system32, которых лично у меня оказалось всего 36 штук. Здесь ID определяет группу (будет одинакова во всех осках):
1.2. Предпочтительный язык интерфейса
Одним из компонентов загрузчика РЕ-файлов является лоадер ресурсов. Он оперирует такими api как
LoadResource(), LoadString(), LoadCursor(), и многими другими. Именно эти функции принимают участие в создании обычных и диалоговых окон, а значит невидимой нитью связаны и с системной локалью LCID.На этапе отображения окна
CreateWindow() или DialogBoxParam(), система начинает перебирать список предпочтительных языков - его возвращает функция из либы Kernel32.dll GetSystemPreferredUILanguages(). Получив самый первый язык из этого списка, система пытается создать окно программы на нём и если прокол, то берёт следующий в очереди, и т.д. Важно понять, что приоритет отдаётся именно первому языку из предпочтительных Preferred.Но что примечательно, ОС предлагает нам не только запросить этот список, но и функцией
SetThreadPreferredUILanguages() выставить в нём приоритетный язык для любого потока программы (а их у процесса может быть несколько). Если это действительно так, то не затрачивая особых усилий мы сможем написать мультиязычное приложение. Главное требование - сменить предпочтительный язык до запуска окна или формы, поскольку на уже отображённую в память форточку api никак не действует, хотя и возвращает ОК.2. Пример многоязычной программы с разными ресурсами
Теперь проверим этот тезис на практике.
Значит алгоритм программы должен выглядеть примерно так:
1. Создаём обычное диалоговое окно на русском языке, с описанием его элементов в секции-ресурсов.
2. Создаём в ресурсах и "Меню" окна тоже на русском, чтобы была возможность менять язык интерфейса.
3. Создаём в ресурсах дубликаты и диалогового окна, и меню.
4. Меняем все текстовые строки в дубликатах на английский язык.
5. В обработчике меню уничтожаем
DestroyWindow() текущее окно.6. В обработчике меню меняем предпочтительный язык с русского на английский.
7. В обработчике меню создаём новое окно!
8. Главное - не перепутать местами пункты 6 и 7, иначе фокус не пройдёт.
Теперь поговорим о нюансах.
Суть в том, что мы определяем по 2 ресурса на разных языках LCID, но обязательно с одинаковым ID самих ресурсов. Вот пример из моей демки:
C-подобный:
section '.rsrc' data resource readable
directory RT_MENU, menus,\
RT_DIALOG, dialogs
resource menus, 037, LANG_RUSSIAN + SUBLANG_RUSSIAN_RUSSIA, rusMenu,\
037, LANG_ENGLISH + SUBLANG_ENGLISH_US, engMenu
resource dialogs, 100, LANG_RUSSIAN + SUBLANG_RUSSIAN_RUSSIA, rusForm,\
100, LANG_ENGLISH + SUBLANG_ENGLISH_US, engForm
Здесь видно, что ID обоих ресурсов "Menu" равен 37, а вот локали LCID будут у них уже разные - для LANG_RUSSIAN получим
0x419, а для LANG_ENGLISH 0x409. Аналогичную картину наблюдаем и в случае ресурса "Dialogs".Чтобы убедиться в правильном оформлении ресурсов, можно вскормить прожку вьюверу "CFF-Explorer". И точно.. видим 2 дира с
ID=4 aka menus, и 5 aka dialogs, ID самих ресурсов 37 и 100, и в каждом из них по 2 языка с LCID=1033=0x409 для Eng, плюс LCID=1049=0x419 для версии Ru. Это значит, что мы всё сделали правильно!Как результат, в момент запуска нашего приложения загрузчик ресурсов запросит у системы предпочтительный язык LCID, найдёт его в нашей секции-ресурсов, и магическим образом сам загрузит нужное окно. Всё что требуется от нас - это при смене языка в меню, тут-же подменить и предпочтительный LCID. Кстати вот идентификаторы каталогов в ресурсах, которые определены на глобальном уровне всех операционных систем Windows (см.разделы AKA выше).
А так должно выглядеть описание ресурсов на разных языках:
C-подобный:
dialog rusForm,'** MUI диалог v0.1 **',0,0,180,162, WS_CAPTION + WS_SYSMENU + DS_CENTER,,,'Verdana',8
dialogitem 'Static','Процессор', -1,010,005,170,010, WS_VISIBLE + SS_CENTER
dialogitem 'Static','',ID_CPU, 010,020,160,010, WS_VISIBLE + SS_CENTER+SS_SUNKEN
dialogitem 'Static','Ядер:', -1,010,035,030,010, WS_VISIBLE + SS_LEFT
dialogitem 'Static','',ID_Cores, 035,035,023,009, WS_VISIBLE + SS_CENTER+SS_SUNKEN
dialogitem 'Static','Частота:', -1,065,035,040,010, WS_VISIBLE + SS_RIGHT
dialogitem 'Static','',ID_Freq, 110,035,060,009, WS_VISIBLE + SS_CENTER+SS_SUNKEN
dialogitem 'Button','Выход',IDCANCEL,010,125,160,013,WS_VISIBLE + BS_DEFPUSHBUTTON
enddialog
;//---------------------------
dialog engForm,'** MUI Dialog v0.1 **',0,0,180,162, WS_CAPTION + WS_SYSMENU + DS_CENTER,,,'Verdana',8
dialogitem 'Static','Processor', -1,010,005,170,010, WS_VISIBLE + SS_CENTER
dialogitem 'Static','',ID_CPU, 010,020,160,010, WS_VISIBLE + SS_CENTER+SS_SUNKEN
dialogitem 'Static','Cores:', -1,010,035,030,010, WS_VISIBLE + SS_LEFT
dialogitem 'Static','',ID_Cores, 040,035,020,009, WS_VISIBLE + SS_CENTER+SS_SUNKEN
dialogitem 'Static','Frequency:',-1,065,035,040,010, WS_VISIBLE + SS_RIGHT
dialogitem 'Static','',ID_Freq, 110,035,060,009, WS_VISIBLE + SS_CENTER+SS_SUNKEN
dialogitem 'Button','Exit',IDCANCEL,010,125,160,013, WS_VISIBLE + BS_DEFPUSHBUTTON
enddialog
3. Пример многоязычной программы с внешними файлами
Предложенный выше вариант многоязычной программы имеет 1 основной недостаток - для добавления новых языков требуется полная перекомпиляция исходного кода. Чтобы решить эту проблему, можно создать единственное дефолтное окно, а при запросе на смену языка, читать текстовые строки из заранее подготовленных внешних файлов. Тогда, чтобы добавить в программу новый язык, достаточно будет пользователю самому создать обычный файл с расширением LNG, в котором будет храниться пара
[Ключ=Значение] примерно в таком виде:
C-подобный:
;//-- Файл Russian.lng -----
[Language]
Name=Russian
[MainWindow]
3001=Процессор
3002=Ядер:
3003=Частота:
3004=Физическая DDR-SDRAM:
3005=Размер файла подкачки:
3006=Виртуальная память:
3007=Свободно физической:
3008=Выход
;//-- Файл France.lng ------
[Language]
Name=France
[MainWindow]
3001=Processeur
3002=Coeurs:
3003=La'cadence:
3004=DDR memoire physique:
3005=Taille du fichier d'echange:
3006=Memoire virtuelle:
3007=Memoire physique libre:
3008=Sortie
Если приглядеться, то формат файлов LNG является точной ксерокопией файлов инициализации INI, поэтому всё сказанное ниже можно применить и в контексте создания файлов конфига CFG, т.к. вся эта тройка одного поля ягоды. Кстати данная техника находит широкое применение во всей программной области, например в механизмах подключения плагинов к уже готовому софту лишь с тем отличием, что вместо txt здесь используют dll.
Раз уж сами майки предлагают нам форматы файлов
cfg\ini\lng, то естественно и в ОС имеется их системная поддержка в лице api Get\SetPrivateProfileString(). На MSDN сказано, что мол функа эта только для Win16 и нужно хранить инфу не в файлах, а в реестре. Но для нашего случая реестр не преемлен в принципе, тем-более что указанная api до сих пор благополучно экспортируется из Kernel32.dll даже на Win10-11.
C-подобный:
DWORD GetPrivateProfileString( //<--- возвращает длину скопированной в буфер строки!
[in] LPCTSTR lpAppName, // имя раздела в файле - в данном случае "Language" или "MainWindow"
[in] LPCTSTR lpKeyName, // имя ключа - здесь "Name" или "3001" и т.д.
[in] LPCTSTR lpDefault, // что выводить, если ключ не найден (строка по умолчанию, обычно "пробел")
[out] LPTSTR lpReturnedString, // линк на приёмный буфер, куда вернётся значение ключа
[in] DWORD nSize, // размер буфа (см.строку с макс длиной в файле)
[in] LPCTSTR lpFileName // полный путь с именем LNG-файла
);
Единственная проблема - функцию нужно вызывать для каждого ключа в файле, т.к. за один выстрел она возвращает только одно значение строки по указанному ключу, которое нужно будет сразу отправить в элемент окна через
SendMessage(). Поэтому на практике используют не осмысленные имена ключей типа StaticCpu=Процессор, а строковое представление последовательных чисел как в данном примере 3001=Процессор и т.д. Именно такой подход позволяет читать сразу все строки в цикле, иначе создание многоязычного интерфейса по такому методу превратится в настоящий ад, ведь в реальных программах могут быть сотни строк.Вот алгоритм, что нам придётся реализовать:
1. Подготовить несколько файлов LNG на разных языках.
2. Создать обычным способом окно приложения (у меня диалоговое в ресурсах).
3. Функцией
FindNextFile() найти все LNG в текущей директории (требуется цикл).4. На каждой итерации цикла выше, прочитать ключи "Name" из найденного LNG, и сохранить их в своей базе.
5. Отобразить основное окно приложения.
6. Читая созданную базу, через
AppendMenu() динамически создать пункты подменю для языков.7. При выборе языка в меню, из соответствующего файла LNG прочитать все строки, перезаписывая ими элементы окна.
Не смотря на то, что данный метод реализации MUI-интерфейса немного сложнее предыдущего, он позволяет малой кровью создать приложение на абсолютно любом языке мира - просто поместите рядом с EXE свой файл LNG, и опишите в нём соответствующие строки. При этом нужно будет строго придерживаться дефолтного формата.
В частности, значение ключа "Name" должно обязательно совпадать с именем LNG-файла, т.к. динамическое создание пунктов меню и открытие файлов для чтения используют одно и то-же имя. Ради прикола попробуйте создать файл со-строками от фонаря типа
3001=Груша, 3002=Автомобиль, и т.д. - код не проверят смысла строк, а потому будет тупо отображать то, что ему попадётся под руку.4. Выводы и рекомендации
Создать в своей программе многоязычный интерфейс достаточно просто, и теперь дело за малым - придумать такой софт, чтобы он был интересен пользователям во всём мире. Тогда его начнут переводить на различные языки, что существенно повысит планку дружелюбности по отношению к юзерам. В скрепке найдёте три исходника на ассемблере fasm представленных здесь демок, а так-же уже скомпилированные их бинари для тестов. Всем удачи, пока!