_http://msdn.microsoft.com/en-us/library/ms794533(printer).aspx Код (Text): include '%fasm%\win64a.inc' ; w.inc ??? start: fastcall [Sleep],10000 cdecl [ExitProcess],0 data import library kernel32,'KERNEL32.DLL',shell32,'SHELL32.DLL' include '%fasm%\api\shell32.inc' ; ??? include '%fasm%\api\kernel32.inc' ; ??? include '%fasm%\equates\shell64.inc' include '%fasm%\equates\kernel64.inc' end data Чего тут ещё нада?
Значиться без 'format PE64' нельзя! Понятно. cdecl - это не прёт, а fastcall всё же поддерживается! Ну собсно там так и написанно. а вот это для чего папки? ; include '%fasm%\equates\shell64.inc' ; include '%fasm%\equates\kernel64.inc'
Проверил на системе. fastcall тоже не работает, только родной invoke исполняется как положено! Ну а stdcall не проходит даже кампиляцию, ато тут есть любители [Sleep] таких вещей ;-
сорри за оффтоп - шутка по поводу названия Две проблемы: 1) трезвый не могу начать, 2)пьяный не могу кончить
invoke такой же родной, как fastcall. cdecl [ExitProcess] - это вообще круто. Хорошо еще, что ExitProcess. Я же тебе советовал демки посмотреть. При включении win64a.inc все заголовочные файлы для библиотечных 64-разрядных функций включаются автоматически, поэтому не нужны как раз-таки последние два включения, а не те, что перед ними. А в папке api фактически находятся файлы с таблицами импорта - они нужны для того, чтобы вручную не прописывать в таблице все используемые в программе api-функции. А вообще для лучшего пониманимания я бы посоветовал сначала написать простейшую программу с минимальным использованием макросов, а уже потом ознакомиться с содержимым макросов и примерами их использования. Не мешало бы ознакомиться и с бинарным интерфейсом обращения к библиотечным функциям. Может, после этого ты вообще отойдешь от использования всяких invoke-ов и fastcall-ов и начнешь использовать действительно родной call.
; поэтому не нужны как раз-таки последние два включения, я это понял, просто стало интересно что за инклюды там. ; Может, после этого ты вообще отойдешь от использования всяких invoke это действительно интересно, однако на msdn я могу находить сишное описание, что быстрее можно воплотить в код так как есть. я пока не решаюсь влезть в стек и боюсь потратить время ничего не поняв, хотя от этого у меня и проблемы. ; Я же тебе советовал демки посмотреть я посмотрел, именно там я заметил, что даже fastcall нигде не было, всё как обычно! моя ошибка была лишь в том, что я format PE64 не включил, собсно ясно, так как это видимо создаёт stub x64, а в 32 битном видимо это дефолт, поэтому можно не указывать. Однако, как-то даже не интересно стало, что так и не попробовал fastcall вызвать "Given the expanded register set, using a FASTCALL methodology of calling convention and a RISC-based exception-handling model makes much more sense."
В этом нет ничего сложного, а знать соглашения о вызове необходимо. stdcall - вызывает функцию по адресу. Параметры заносятся в стек в обратном порядке то есть Код (Text): stdcall Beep,2000,100 эквивалентно Код (Text): push 100 push 2000 call Beep invoke - вызывает функцию по соглашению stdcall из переменной расположенной в памяти то есть Код (Text): invoke Beep,2000,100 эквивалентно Код (Text): push 100 push 2000 call [Beep]
cominvk - вызывает функцию по адресу, расположеному виртуальной таблице методов (VMT). Параметры заносятся в стек в обратном порядке, последним заносится указатель на экземпляр класса. Адрес экземпляра класса хранится в памяти. Код (Text): cominvk OPCServerList,EnumClassesOfCategories,3,CATIDS,esi,esi,EnumGUID Превратится в Код (Text): push EnumGUID push esi push esi push CATIDS push 3 mov eax,[OPCServerList] push eax mov eax,[eax] call dword[eax+12] ;12 - это смещение метода EnumClassesOfCategories в виртуальной таблице
Товарищи, речь идет о Win64. Там в первую очередь используется регистровая передача параметров. А в программах под Win32 я тоже использую простейший stdcall, который периодически изменяю в зависимости от используемого в конкретной программе способа вызова библиотечных функций (например, напрямую через таблицу или через заглушки вида jmp [ApiFunction]). В фасме формат выходного файла определяется именно директивой format внутри исходника. Дефолтом, насколько я знаю, используется flat binary / use16. Если ты где-то не увидел format PE, то возможно она скрыта в инклудах. stub - это лишь один из необязательных параметров format.
Я попробовал без него и увидел, что так работает, ну и решил что не нужно Хотя use16 меня не устраивает, тогда будем делать format PE иль use32 укажу. Порядок погрузки параметров в стек знаком с детства, хотя всякие директивы иль команды, видимо могут изменять этот прорядок, отсюда наверное всяки вариации invoke cinvoke, но с этим проще, так как это просто можно понять и запомнить. Но дело в другом! Откуда знать мне адрес? Причём в отношении, ну хотяб ExitProcess или Beep. Он что постоянный? Или его не надо знать? Или в Windows это регламентированно. На msdn только прототипы функций говорят. Точно так же теоретически понятно, что вместо памяти используем регистры, но в связи с использованием API это так же туманно, чем их заполнять - что передавать... Помню читал про DOS, там API =) порегистрово всё расписывалось куда-что как! Там другая тема кароче, ближе к асму, но практически не дающая ничего мне в WinAPI... Иль я не прав? cominvk - вот это я видел, думал глюк какой )) но после моих исследований cdecl постеснялся спросить, ато засмеют =) murder, всётаки ты хорошо про stdcall, объяснил кое что у меня прояснилось! Но надо будет это как-то с апи связать конкретно, ато из головы вошло и вышло в другое ухо будет.
Он в таблице импорта. Код (Text): data import library kernel32,'KERNEL32.DLL',shell32,'SHELL32.DLL' include '%fasm%\api\shell32.inc' ; ??? include '%fasm%\api\kernel32.inc' ; ??? include '%fasm%\equates\shell64.inc' include '%fasm%\equates\kernel64.inc' В общем для win32 используется invoke. Win64 не знаю.
stdcall на самом деле может быть определен любым способом. Традиционно это слово просто определяет порядок помещения параметров в стек. Лично я использую stdcall в виде: Код (Text): stdcall ApiFunction, parameters Но в зависимости от текущей необходимости в разных программах за этой записью могут быть скрыты различные способы вызова. Далее тебе нужно почитать про формат таблицы экспорта. В конечном итоге все сводится к массиву двойных слов (PE) / учетверенных слов (PE64) с нулевым завершающим элементом для каждой библиотеки. До запуска программы каждый элемент массива (кроме терминатора) содержит ссылку на спец. структуру, содержащую имя функции и ее предполагаемый экспортный номер (может содержать 0, тогда настройка будет выполняться только по имени). В процессе запуска программы загрузчик заменяет в каждом элементе массива (кроме терминатора) ссылку на структуру, определяющую импортируемую функцию ее адресом. Таким образом, после настройки мы получаем массив с адресами импортируемых библиотечных функций. Если каждому элементу массива дать символическое имя, соответствующее функции, адрес которой находится в этом элементе, то вызов функции можно выполнять так: Код (Text): call [ApiFunction] ; синтаксис fasm ... ApiFunction dd _ApiFunction ; после запуска здесь будет адрес функции ... _ApiFunction dw 0 ; экспортный номер или 0 db "ApiFunctionA", 0 ; нольтерм. строка с реальным именем функции Здесь все проще, чем в DOS: в rcx - первый параметр; в rdx - второй; в r8 - третий; в r9 - четвертый; далее в стеке. Важно помнить, что в стеке нужно резервировать место в том числе и для регистровых параметров, но помещать их в стек не надо. Если параметр, передаваемый в регистре, имеет разрядность меньше 64 бит, то для его передачи можно и не использовать весь 64-разрядный регистр, например, для 32-разрядных параметров можно использовать ecx, edx, r8d, r9d. Вот здесь вроде бы подробно разобрана фасмовская демка.