Функция точки входа DLL
Перевод с английского статьи от 31.05.2018 г. «Dynamic-Link Library Entry-Point Function»:
https://docs.microsoft.com/ru-ru/windows/win32/dlls/dynamic-link-library-entry-point-function
(На данный момент на этом сайте нет перевода этой статьи на русский, есть только версия на английском.)
Функция точки входа для DLL необязательна, но может быть определена. Если эта функция присутствует, операционная система вызывает эту функцию точки входа всякий раз, когда процесс или поток выполнения приложения загружает или выгружает DLL. Эта функция может использоваться для выполнения задач простых инициализации и очистки. Например, эта функция может создать локальное хранилище потока [thread local storage] при создании нового потока и ликвидировать его при завершении работы потока.
Если вы компонуете свою DLL с библиотекой времени выполнения для языка Си [C run-time library] , то эта библиотека времени выполнения может создать функцию точки входа за вас, при этом вы можете написать свою отдельную функцию инициализации. Для более подробной информации об этом читайте документацию по вашей библиотеке времени выполнения [полезная статья по этому вопросу: «Библиотеки DLL и поведение библиотеки времени выполнения Visual C++»] .
Как определить точки входа и выхода в криптовалютах
Если вы хотите писать свою собственную функцию точки входа, используйте для этого функцию DllMain. Название DllMain является умолчательной заглушкой для функции точки входа, определяемой пользователем. Вы должны указать действительное имя функции точки входа, которое вы используете, при сборке своей DLL. Более подробную информацию об этом ищите в документации, поставляемой с вашими инструментами разработки [полезные статьи по этому вопросу: «Библиотеки DLL и поведение библиотеки времени выполнения Visual C++», «Параметр /ENTRY компоновщика»] .
Ситуации, в которых вызывается функция точки входа
- процесс загружает DLL. Для процессов, использующих динамическое связывание во время запуска программы, DLL загружается во время инициализации процесса. Для процессов, использующих динамическое связывание во время выполнения программы, DLL загружается перед тем, как функция LoadLibrary или LoadLibraryEx возвратит значение [завершит свою работу] ;
- процесс выгружает DLL. DLL выгружается, когда процесс завершается или вызывает функцию FreeLibrary и счетчик ссылок по данной DLL становится равным нулю. Если процесс завершается из-за вызова функций TerminateProcess или TerminateThread, операционная система не вызывает функцию точки входа DLL;
- в процессе, который загрузил DLL, создается новый поток выполнения. Можно использовать функцию DisableThreadLibraryCalls, чтобы отключить уведомления о создании новых потоков выполнения;
- поток выполнения процесса, загрузившего DLL, завершается нормально (то есть не из-за вызова функций TerminateThread или TerminateProcess). Когда процесс выгружает DLL, функция точки входа вызывается только один раз для всего процесса, а не по разу на каждый существующий поток выполнения процесса. Можно использовать функцию DisableThreadLibraryCalls, чтобы отключить уведомления о завершении потоков выполнения.
Операционная система вызывает функцию точки входа в контексте процесса или потока выполнения, ставшего причиной вызова функции. Такой подход разрешает DLL использовать ее функцию точки входа для резервирования памяти в виртуальном адресном пространстве вызывающего процесса или открывать дескрипторы, доступные процессу. Функция точки входа может также резервировать память, которая является собственной [то есть закрытой для других] для нового потока выполнения посредством использования локального хранилища потока [thread local storage (TLS)] . Более подробную информацию о локальном хранилище потока ищите в статье «Локальное хранилище потока».
ЛУЧШИЕ и простые точки входа.
Написание функции точки входа
Функция точки входа DLL должна быть объявлена с применением соглашения о вызове stdcall [для операционных систем компании Microsoft — __stdcall] . Если точка входа DLL не будет объявлена правильно, DLL не загрузится, а операционная система выдаст сообщение, указывающее, что точка входа DLL должна быть объявлена с применением макроса WINAPI .
- процесс загружает DLL (эта причина вызова функции точки входа DLL обозначается константой DLL_PROCESS_ATTACH );
- текущий процесс создает новый поток выполнения ( DLL_THREAD_ATTACH );
- поток выполнения завершается нормально ( DLL_THREAD_DETACH );
- процесс выгружает DLL ( DLL_PROCESS_DETACH ).
Из-за того, что системная библиотека Kernel32.dll гарантированно будет загружена в адресное пространство процесса, когда вызывается функция точки входа, вызов функций из Kernel32.dll не приведет к тому, что DLL будет использована раньше, чем выполнится код ее инициализации. Поэтому функция точки входа может создавать объекты синхронизации, такие как критические секции и мьютексы, и использовать TLS [локальное хранилище потока] , потому что эти функции размещены в Kernel32.dll . Не является безопасным, к примеру, вызов функций реестра [операционной системы] , потому что они размещены в системной библиотеке Advapi32.dll .
Вызов других функций может привести к проблемам, которые трудно диагностировать. Например, вызов функций [компонентов операционной системы Windows] «User» [функции для работы с пользовательским интерфейсом, окнами] , «Shell» [функции для работы с командной оболочкой операционной системы] и функций COM [Component Object Model, по-русски «модель компонентного объекта»] может послужить причиной ошибок нарушения доступа из-за того, что некоторые функции в их DLL вызывают функцию LoadLibrary, чтобы загрузить другие компоненты операционной системы. И, наоборот, вызов этих функций во время завершения [процесса или потока выполнения] может послужить причиной ошибок нарушения доступа из-за того, что соответствующий компонент мог уже быть выгружен или не инициализирован.
Следующий пример демонстрирует примерную структуру функции точки входа DLL.
BOOL WINAPI DllMain( HINSTANCE hinstDLL, // дескриптор модуля DLL DWORD fdwReason, // причина вызова функции LPVOID lpReserved ) // параметр с дополнительной информацией < // выполнить некие действия в зависимости от причины вызова функции switch( fdwReason ) < case DLL_PROCESS_ATTACH: // инициализация для каждого нового процесса; // возврат значения FALSE, если не получается загрузить DLL break; case DLL_THREAD_ATTACH: // выполнить инициализацию для потока выполнения break; case DLL_THREAD_DETACH: // произвести очистку для потока выполнения break; case DLL_PROCESS_DETACH: // выполнить любую необходимую очистку break; > return TRUE; // успешная отработка функции по причине DLL_PROCESS_ATTACH >
Возвращаемое значение функции точки входа
Когда функция точки входа DLL вызывается по причине загрузки процесса, эта функция возвращает значение TRUE, чтобы показать, что она отработала успешно. Для процессов, использующих динамическое связывание во время запуска программы, возврат значения FALSE повлечет за собой неудачу инициализации процесса и его завершение. Для процессов, использующих динамическое связывание во время выполнения программы, возврат значения FALSE повлечет за собой то, что функция LoadLibrary или LoadLibraryEx возвратит значение NULL, указывающее на то, что эта функция свою работу не выполнила по причине некой ошибки. (Операционная система сразу вызовет нашу функцию точки входа с причиной вызова DLL_PROCESS_DETACH и выгрузит DLL.) Возвращаемое значение функции точки входа оставляется без внимания, когда функция вызывается по любой другой причине, кроме DLL_PROCESS_ATTACH.
Источник: ilyachalov.livejournal.com
Консольные приложения
Из этой статьи вы узнаете, как структурировать консольное приложение F#.
Неявная точка входа
По умолчанию приложения F# используют неявную точку входа. Например, для следующей программы точка входа неявна и, когда программа выполняется, код выполняется из первой строки до последней:
open System let printSomeText() = let text = «Hello» + «World» printfn $»text = » let showCommandLineArgs() = for arg in Environment.GetCommandLineArgs() do printfn $»arg = » printSomeText() showCommandLineArgs() exit 100
Явная точка входа
При желании можно использовать явную точку входа. Обычно это делается по одной или всем следующим причинам:
- Вы предпочитаете получить доступ к аргументам командной строки через аргумент, переданный функции, а не использовать System.Environment.GetCommandLineArgs() .
- Вы хотите вернуть код ошибки с помощью результата возврата, а не использовать exit .
- Вы хотите выполнить модульное тестирование кода в последнем файле консольного приложения.
В следующем примере показана простая main функция с явной точкой входа.
[] let main args = printfn «Arguments passed to function : %A» args // Return 0. This indicates success. 0
При выполнении этого кода с помощью командной строки EntryPoint.exe 1 2 3 выходные данные приведены ниже.
Arguments passed to function : [|»1″; «2»; «3»|]
Синтаксис
[] let-function-binding
Remarks
В предыдущем синтаксисе let-function-binding — это определение функции в привязке let .
Точка входа в программу, скомпилированную как исполняемый файл, — это место, где выполнение официально начинается. Чтобы указать точку входа в приложение F#, примените EntryPoint атрибут к функции программы main . Эта функция (созданная с помощью let привязки) должна быть последней функцией в последнем скомпилированном файле. Последний скомпилированный файл — это последний файл в проекте или последний файл, передаваемый в командную строку.
Функция точки входа имеет тип string array -> int . Аргументы, предоставленные в командной строке, передаются main функции в массиве строк. Первый элемент массива является первым аргументом; Имя исполняемого файла не включено в массив, так как оно находится на некоторых других языках. Возвращаемое значение используется в качестве кода выхода для процесса. Ноль обычно указывает на успех; ненулевое значение указывает на ошибку. Нет соглашения о конкретном значении ненулевых кодов возврата; значения кодов возврата зависят от приложения.
Источник: learn.microsoft.com
Точка входа программы
Текст программы начинается с инструкции #include, которая позволяет включить в программу заголовочный файл WINDOWS.H:
WINDOWS.H включает в себя много других заголовочных файлов, содержащих объявления функций Windows, структур Windows, новые типы данных и числовые константы.
За инструкцией #includeследует объявлениеWndProc:
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
Это объявление в начале программы необходимо потому, что в тексте функции WinMainимеются ссылки на функциюWndProc.
В программе на языке С, написанной для традиционной среды, точкой входа является функция main. С этого места программа начинает выполняться. (Фактически функция main является точкой входа в ту часть программы, которая пишется программистом. Обычно компилятор С должен вставить некоторый стартовый код в исполняемый файл. Этот код и вызывает функциюmain.) Точкой входа программы для Windows является функцияWinMain.WinMain всегда определяется следующим образом:
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
Эта функция использует последовательность вызовов WINAPI и, по своему завершению, возвращает операционной системе Windows целое. Функция называется WinMain. В ней есть четыре параметра.
Параметр hInstanceназывается описателем экземпляра (instance handle). Это уникальное число, идентифицирующее программу, когда она работает под Windows. Может так случиться, что пользователь запустит под Windows несколько копий одной и той же программы. Каждая копия называется «экземпляром» и у каждой свое значениеhInstance. Описатель экземпляра можно сравнить с «идентификатором задачи» или «идентификатором процесса» — обычными терминами многозадачных операционных систем.
Параметр hPrevInstance—всегда равен NULL.
Параметр szCmdLine— это указатель на оканчивающуюся нулем строку, в которой содержатся любые параметры, переданные в программу из командной строки. Вы можете запустить программу для Windows с параметром командной строки, вставив этот параметр после имени программы в командной строке MS-DOS или указать имя программы и параметр в окне диалога Run, которое вызывается из меню Start.
Параметр iCmdShow— число, показывающее, каким должно быть выведено на экран окно в начальный момент. Это число задается при запуске программы другой программой. Программисты достаточно редко обращаются к этому числу, но при необходимости такая возможность существует. В большинстве случаев число равно 1 или 7. Но лучше не думать об этом значении как о единице или как о семерке.
Лучше думайте о них как об идентификаторе SW_SHOWNORMAL (заданном в заголовочных файлах Windows равным 1) или идентификаторе SW_SHOWMINNOACTIVE (заданном равным 7). Префикс SW в этих идентификаторах означает «показать окно» (show window). Параметр показывает, необходимо ли запущенную пользователем программу выводить на экран в виде окна нормального размера или окно должно быть изначально свернутым.
Оконная процедура
Вы, несомненно, уже привыкли к мысли, что программа делает вызовы операционной системы. Таким образом, например, программа открывает файл на жестком диске. Но возможно и обратное, когда операционная система вызывает программу. Это непривычно, тем не менее, это суть объектно-ориентированной архитектуры Windows. При программировании для Windows вы фактически занимаетесь одним из видов объектно-ориентированного программирования.
У каждого окна, создаваемого программой, имеется соответствующая оконная процедура. Windows посылает сообщение окну путем вызова оконной процедуры, на основе этого сообщения окно совершает какие-то действия и затем возвращает управление Windows. «Windows посылает программе сообщение» — имеется в виду, что Windows вызывает функцию внутри программы. Параметры этой функции описывают параметры сообщения. Эта функция, находящаяся в вашей программе для Windows, называется оконной процедурой (window procedure). Все это называется ”Архитектура, управляемая событиями”.
Более точно, окно всегда создается на основе»класса окна». Класс окна определяет оконную процедуру,обрабатывающую поступающие окну сообщения.
Использование класса окна позволяет создавать множество окон на основе одного и того же класса окна и, следовательно, использовать одну и ту же оконную процедуру.Например, все кнопки во всех программах дляWindows созданы на основе одного и того же класса окна. Этот класс связан с оконной процедурой(расположенной в динамически подключаемой библиотекеWindows), которая управляет процессом передачи сообщений всем кнопкам всех окон.В объектно-ориентированном программировании любой»объект» несет в себе сочетание кода и данных.
Окно— это объект. Код— это оконная процедура. Данные— это информация, хранимая оконной процедурой, и информация,хранимая системойWindows для каждого окна и каждого класса окна, которые имеются в системе.Оконная процедура обрабатывает сообщения, поступающие окну.
Очень часто эти сообщения передают окну информацию о том, что пользователь осуществил ввод с помощью клавиатуры или мыши. Таким образом,например, кнопки»узнают» о том, что они нажаты. Другие сообщения говорят окну о том, что необходимо изменить размер окна или о том, что поверхность окна необходимо перерисовать.Когда программа дляWindows начинает выполняться, Windows строит для программы очередь сообщений(messagequeue). В этой очереди хранятся сообщения для любых типов окон, которые могли бы быть созданы программой.Небольшая часть программы, которая называется циклом обработки сообщений(message loop), выбирает эти сообщения из очереди и переправляет их соответствующей оконной процедуре. Другие сообщения отправляются непосредственно оконной процедуре минуя очередь сообщений.
Источник: studfile.net
Точки входа в Python
Многие думают, что точки входа это такие инструкции в setup.py, которые позволяют сделать пакет доступным для запуска из командной строки. Это, в целом, верно, но возможности точек входа не ограничиваются этим.
Ниже я покажу как можно реализовать систему плагинов для пакета, чтобы другие люди могли с ним взаимодействовать или, например, расширять его функциональность динамически.
Осторожно: специфический юмор далее по тексту.
ООО «Змейка»
Поздравляю! Вас только что назначили главой ООО «Змейка». Это очень ответственная должность, ударить в грязь лицом нельзя, а значит, нужно как можно скорее дать указание отделу разработки приступить к созданию прототипа продукта. И вот, лучшие умы компании начинают работу над snek.py:
ascii_snek = «»» —. _ _,.—. `’.’. .’`__ o `;__. ‘.’. .’.’` ‘—‘` ` ‘.`’—. —‘`.’ `’—. —‘` «»» def main(): print(ascii_snek) if __name__ == ‘__main__’: main()
Чуть позже, на собрании акционеров вы с гордостью демонстрируете первые результаты!
$ python snek.py —. _ _,.—. `’.’. .’`__ o `;__. ‘.’. .’.’` ‘—‘` ` ‘.`’—. —‘`.’ `’—. —‘`
Змейка как сервис
К сожалению, рядовой потребитель пока ещё не освоил Python и хочет запускать программу из консоли, не думая об интерпретаторе или местоположении snek.py. Что ж, наши лучшие специалисты на то и лучшие, что смогли запаковать скрипт так, чтобы он автоматически создавал консольную команду при установке.
Для создания распространяемого пакета нам нужен файл setup.py, где содержится информация о зависимостях, лицензии и т.д. Помимо этого, в нём можно указать точки входа:
from setuptools import setup setup( name=’snek’, entry_points= < ‘console_scripts’: [ ‘snek = snek:main’, ], >)
console_scripts, как пояснили специалисты, специальная точка входа. setuptools читает её элементы как » = «, создавая для каждого элемента консольную утилиту при установке пакета.
Пока что давайте установим скрипт из исходников:
$ python setup.py develop running develop running egg_info writing snek.egg-infoPKG-INFO writing dependency_links to snek.egg-infodependency_links.txt writing entry points to snek.egg-infoentry_points.txt writing top-level names to snek.egg-infotop_level.txt reading manifest file ‘snek.egg-infoSOURCES.txt’ writing manifest file ‘snek.egg-infoSOURCES.txt’ running build_ext Creating c:program files (x86)py36-32libsite-packagessnek.egg-link (link to .) snek 0.0.0 is already the active version in easy-install.pth Installing snek-script.py script to C:Program Files (x86)Py36-32Scripts Installing snek.exe script to C:Program Files (x86)Py36-32Scripts Installing snek.exe.manifest script to C:Program Files (x86)Py36-32Scripts Installed c:usersrachumnotebooks Processing dependencies for snek==0.0.0 Finished processing dependencies for snek==0.0.0
На конференции, посвящённой итогам года, вы выступаете, демонстрируя новейшую разработку:
$ snek —. _ _,.—. `’.’. .’`__ o `;__. ‘.’. .’.’` ‘—‘` ` ‘.`’—. —‘`.’ `’—. —‘`
Змейка в каждый дом
Змейка завоёвывает мир. Компания провела IPO и была оценена в рекордные 60 миллиардов долларов. Хипстеры требуют новую, стильную, модную и молодёжную змейку. А раз есть спрос, будет и предложение:
Хипстеры в восторге:
$ snek —. _ _,.—. `’.’. .’`__ o `;__. ‘.’. .’.’` ‘—‘` ` ‘.`’—. —‘`.’ `’—. —‘` $ snek —type fancy _. _ ‘«««^~»-,_`»-,_ .-~c~-. `~:. ^-. `~~~-.c ; `:. `-, _.-~~^^~:. `. ; _,—~~~~-._ `:. ~. .~ `. .` ;’ .:` `: `:. ` _.:-,. `. .’ .: :’ _.-~^~-. `. `..’ .: `. ‘ : .’ _:’ .-‘ `. :. .: .’`. : ; : `-‘ .:’ `. `^~~^` . `. ; ; `-.__,-~ ~-. ,’ ‘: ‘.__.` :’ ~—..—‘ ‘:. .:’ ‘. ___.:’
Транснациональная Змейка
Миллионы человек не мыслят свой день без змейки. Даже после поглощения Гугла ресурсов ООО «Змейка» не хватает, чтобы удовлетворять потребности пользователей по всему миру. Похоже настало время дать людям возможность создавать собственных змеек на базе нашей инфраструктуры.
Теперь, каждый раз когда snek запущена, она ищет других зарегистрированных в системе змеек, используя точку входа snek_types. Каждая такая змейка зарегистрирована под названием её типа, что позволяет выбирать нужную змейку в зависимости от параметров консоли.
Всё самое главное происходит внутри get_sneks. Вызов pkg_resources.iter_entry_points(‘snek_types’) позволяет пройтись по всем точкам входа, зарегистрированным под именем «snek_types» где-либо. Таким образом, любой сторонний пакет сможет в своём setup.py создать точку входа «snek_types», чтобы быть загруженным нашим скриптом.
Мы рассказали о «snek_types» нашим коллегам из ООО «Змеиные решения», и они тут же начали создавать змейку своей мечты. Вот каким получился их пакет cute_snek.py:
cute_snek = r»»» /^/^ _|__| O| / /~ _/ ____|__________/ _______ ` | | / / / / / / / / / / _—-_ / / _-~ ~-_ | | ( ( _-~ _—_ ~-_ _/ | ~-____-~ _-~ ~-_ ~-_-~ / ~-_ _-~ ~-_ _-~ ~—______-~ ~-___-~ «»»
А вот как они реализовали свой setup.py, чтобы наша snek могла загрузить их змейку:
from setuptools import setup setup( name=’cute_snek’, entry_points= < ‘snek_types’: [ ‘cute = cute_snek:cute_snek’, ], >)
Они зарегистрировали переменную cute_snek в модуле cute_snek под именем cute. Далее они устанавливают пакеты snek и cute_snek:
$ cd cute_snek python setup.py develop running develop running egg_info writing cute_snek.egg-infoPKG-INFO writing dependency_links to cute_snek.egg-infodependency_links.txt writing entry points to cute_snek.egg-infoentry_points.txt writing top-level names to cute_snek.egg-infotop_level.txt reading manifest file ‘cute_snek.egg-infoSOURCES.txt’ writing manifest file ‘cute_snek.egg-infoSOURCES.txt’ running build_ext Creating c:program files (x86)py36-32libsite-packagescute-snek.egg-link (link to .) cute-snek 0.0.0 is already the active version in easy-install.pth Installed c:usersrachumcute_snek Processing dependencies for cute-snek==0.0.0 Finished processing dependencies for cute-snek==0.0.0
Теперь, запустив snek, они могут вывести свою змейку из пакета cute_snek за счёт её динамической загрузки по точке входа:
$ snek —type cute /^/^ _|__| O| / /~ _/ ____|__________/ _______ ` | | / / / / / / / / / / _—-_ / / _-~ ~-_ | | ( ( _-~ _—_ ~-_ _/ | ~-____-~ _-~ ~-_ ~-_-~ / ~-_ _-~ ~-_ _-~ ~—______-~ ~-___-~
Змейка 2.0
Пока всё внимание высшего руководства уходит на разборки с налоговой инспекцией и антимонопольной службой, отдел разработки наконец-то может выкроить немного времени на рефакторинг кода.
Главный системный архитектор понял, что если сторонние змейки могут быть загружены как плагины, значит, так же могут быть загружены и встроенные змейки.
Уберём особую обработку встроенных змеек:
И взамен зарегистрируем для них универсальные точки входа:
Переустановим изменённую змейку:
$ python setup.py develop running develop running egg_info writing snek.egg-infoPKG-INFO writing dependency_links to snek.egg-infodependency_links.txt writing entry points to snek.egg-infoentry_points.txt writing top-level names to snek.egg-infotop_level.txt reading manifest file ‘snek.egg-infoSOURCES.txt’ writing manifest file ‘snek.egg-infoSOURCES.txt’ running build_ext Creating c:program files (x86)py36-32libsite-packagessnek.egg-link (link to .) snek 0.0.0 is already the active version in easy-install.pth Installing snek-script.py script to C:Program Files (x86)Py36-32Scripts Installing snek.exe script to C:Program Files (x86)Py36-32Scripts Installing snek.exe.manifest script to C:Program Files (x86)Py36-32Scripts Installed c:usersrachumnotebooks Processing dependencies for snek==0.0.0 Finished processing dependencies for snek==0.0.0
$ snek —. _ _,.—. `’.’. .’`__ o `;__. ‘.’. .’.’` ‘—‘` ` ‘.`’—. —‘`.’ `’—. —‘` $ snek —type fancy _. _ ‘«««^~»-,_`»-,_ .-~c~-. `~:. ^-. `~~~-.c ; `:. `-, _.-~~^^~:. `. ; _,—~~~~-._ `:. ~. .~ `. .` ;’ .:` `: `:. ` _.:-,. `. .’ .: :’ _.-~^~-. `. `..’ .: `. ‘ : .’ _:’ .-‘ `. :. .: .’`. : ; : `-‘ .:’ `. `^~~^` . `. ; ; `-.__,-~ ~-. ,’ ‘: ‘.__.` :’ ~—..—‘ ‘:. .:’ ‘. ___.:’ $ snek —type cute /^/^ _|__| O| / /~ _/ ____|__________/ _______ ` | | / / / / / / / / / / _—-_ / / _-~ ~-_ | | ( ( _-~ _—_ ~-_ _/ | ~-____-~ _-~ ~-_ ~-_-~ / ~-_ _-~ ~-_ _-~ ~—______-~ ~-___-~
Источник: habr.com