Я знаю, что фанатам это может показаться довольно простым. Но я хочу прояснить это.
Когда я хочу использовать Win32 DLL, обычно я просто вызываю API, такие как LoadLibrary () и GetProcAdderss (). Но в последнее время я работаю с DirectX9, и мне нужно добавить файлы d3d9.lib , d3dx9.lib и т. Д.
Я достаточно слышал, что LIB предназначен для статического связывания, а DLL - для динамического связывания.
Итак, я понимаю, что LIB содержит реализацию методов и статически связан во время компоновки как часть окончательного EXE-файла. Хотя DLL динамически загружается во время выполнения и не является частью окончательного EXE-файла.
Но иногда вместе с файлами DLL поставляются некоторые LIB -файлы, поэтому:
- Для чего нужны эти файлы LIB?
- Как они достигают того, для чего предназначены?
- Есть ли какие-нибудь инструменты, которые позволят мне проверить внутреннюю структуру этих LIB-файлов?
Обновление 1
После проверки википедии я вспомнил, что эти файлы LIB называются библиотекой импорта . Но мне интересно, как это работает с моим основным приложением и динамически загружаемыми библиотеками DLL.
Обновление 2
Как и сказал RBerteig, в файлах LIB есть некоторый код-заглушка, рожденный вместе с библиотеками DLL. Итак, последовательность вызовов должна быть такой:
Мое основное приложение -> заглушка в LIB -> настоящая целевая DLL
Итак, какая информация должна содержаться в этих LIB? Я мог думать о следующем:
- Файл LIB должен содержать полный путь к соответствующей DLL; Таким образом, DLL может быть загружена во время выполнения.
- Относительный адрес (или смещение файла?) Точки входа каждого метода экспорта DLL должен быть закодирован в заглушке; Так можно было сделать правильные переходы / вызовы методов.
Я прав в этом? Есть что-то еще?
Кстати: есть ли какой-нибудь инструмент, который может проверить библиотеку импорта? Если я это увижу, сомнений больше не будет.
источник
lib /list xxx.lib
иlink /dump /linkermember xxx.lib
. См. Этот вопрос о переполнении стека .dumpbin -headers xxx.lib
обеспечивает некоторую более подробную информацию, по сравнению сlib
иlink
коммунальных услуг.Ответы:
Связывание с файлом DLL может происходить неявно во время компоновки
компиляцииили явно во время выполнения. В любом случае DLL загружается в область памяти процессов, и все ее экспортированные точки входа становятся доступными для приложения.Если используется явно во время выполнения, вы используете
LoadLibrary()
и,GetProcAddress()
чтобы вручную загрузить DLL и получить указатели на функции, которые вам нужно вызвать.Если компоновка выполняется неявно при построении программы, то заглушки для каждого экспорта DLL, используемого программой, связываются с программой из библиотеки импорта, и эти заглушки обновляются по мере загрузки EXE и DLL при запуске процесса. (Да, я здесь немного упростил ...)
Эти заглушки должны откуда-то поступать, а в цепочке инструментов Microsoft они берутся из специальной формы файла .LIB, называемой библиотекой импорта . Требуемый .LIB обычно создается одновременно с DLL и содержит заглушку для каждой функции, экспортируемой из DLL.
Как ни странно, статическая версия той же библиотеки также будет поставляться в виде файла .LIB. Нет тривиального способа отличить их друг от друга, за исключением того, что LIB, которые являются библиотеками импорта для DLL, обычно будут меньше (часто намного меньше), чем соответствующие статические LIB.
Кстати, если вы используете инструментальную цепочку GCC, вам не нужны библиотеки импорта, соответствующие вашим DLL. Версия компоновщика Gnu, перенесенная на Windows, понимает библиотеки DLL напрямую и может синтезировать практически любые необходимые заглушки на лету.
Обновить
Если вы просто не можете устоять перед знанием того, где на самом деле все гайки и болты и что на самом деле происходит, в MSDN всегда есть что-то, что может вам помочь. Статья Мэтта Пьетрека « Углубленный взгляд на формат переносимых исполняемых файлов Win32» - это очень полный обзор формата EXE-файла и того, как он загружается и запускается. Его даже обновили, чтобы охватить .NET и многое другое, с тех пор, как он впервые появился в MSDN Magazine ca. 2002 г.
Кроме того, может быть полезно знать, как точно узнать, какие библиотеки DLL используются программой. Для этого используется Dependency Walker, также известный как Dependency Walker. Его версия включена в Visual Studio, но последняя версия доступна у ее автора по адресу http://www.dependencywalker.com/ . Он может идентифицировать все библиотеки DLL, которые были указаны во время компоновки (как ранняя, так и отложенная загрузка), а также может запускать программу и следить за любыми дополнительными библиотеками DLL, которые она загружает во время выполнения.
Обновление 2
Я переформулировал часть предыдущего текста, чтобы прояснить его при повторном чтении и использовать термины искусства: неявное и явное связывание. для согласованности с MSDN.
Итак, у нас есть три способа сделать библиотечные функции доступными для использования в программе. Тогда очевидным последующим вопросом будет: «Как мне выбрать, какой путь?»
Статическая компоновка - это способ компоновки самой программы. Все ваши объектные файлы перечислены и собраны компоновщиком в EXE-файл. Попутно компоновщик берет на себя мелкие хлопоты, такие как исправление ссылок на глобальные символы, чтобы ваши модули могли вызывать функции друг друга. Библиотеки также могут быть статически связаны. Объектные файлы, составляющие библиотеку, собираются библиотекарем в файл .LIB, в котором компоновщик ищет модули, содержащие необходимые символы. Одним из эффектов статической компоновки является то, что с ней связываются только те модули из библиотеки, которые используются программой; другие модули игнорируются. Например, традиционная математическая библиотека C включает в себя множество функций тригонометрии. Но если вы свяжетесь с ним и используете
cos()
, вы не получите копию кода дляsin()
или,tan()
если вы также не вызвали эти функции. Для больших библиотек с богатым набором функций такое выборочное включение модулей важно. На многих платформах, таких как встроенные системы, общий размер кода, доступного для использования в библиотеке, может быть большим по сравнению с пространством, доступным для хранения исполняемого файла на устройстве. Без выборочного включения было бы труднее управлять деталями построения программ для этих платформ.Однако наличие копии одной и той же библиотеки в каждой запущенной программе создает нагрузку на систему, которая обычно запускает множество процессов. При правильном типе системы виртуальной памяти страницы памяти с идентичным содержимым должны существовать в системе только один раз, но могут использоваться многими процессами. Это создает преимущество для увеличения вероятности того, что страницы, содержащие код, будут идентичны какой-либо странице в максимально возможном количестве других запущенных процессов. Но если программы статически связаны с библиотекой времени выполнения, то каждая из них имеет различное сочетание функций, каждая из которых выложена в карте памяти процессов в разных местах, и не так много кодовых страниц с общим доступом, если только это не программа, которая сама по себе запускать больше, чем просто процесс. Так что идея DLL получила еще одно важное преимущество.
DLL для библиотеки содержит все ее функции, готовые к использованию любой клиентской программой. Если много программ загружают эту DLL, все они могут использовать ее кодовые страницы. Все выигрывают. (Ну, пока вы не обновите DLL новой версией, но это не часть нашей истории. Google DLL Ад за эту сторону истории.)
Итак, первый большой выбор при планировании нового проекта - между динамической и статической связью. Благодаря статической привязке у вас меньше файлов для установки, и вы защищены от третьих лиц, обновляющих используемую DLL. Однако ваша программа больше, и она не так хороша в экосистеме Windows. При динамическом связывании у вас есть больше файлов для установки, у вас могут возникнуть проблемы с третьим лицом, обновляющим используемую вами DLL, но вы обычно более дружелюбны по отношению к другим процессам в системе.
Большим преимуществом DLL является то, что ее можно загружать и использовать без перекомпиляции или даже повторного связывания основной программы. Это может позволить стороннему поставщику библиотеки (например, Microsoft и среде выполнения C) исправить ошибку в своей библиотеке и распространить ее. Как только конечный пользователь устанавливает обновленную DLL, он немедленно получает исправление ошибки во всех программах, использующих эту DLL. (Если это не сломает вещи. См. DLL Hell.)
Другое преимущество заключается в различии неявной и явной загрузки. Если вы приложите дополнительные усилия для явной загрузки, то DLL может даже не существовать, когда программа была написана и опубликована. Это позволяет использовать механизмы расширения, которые могут, например, обнаруживать и загружать плагины.
источник
Эти файлы библиотеки импорта .LIB используются в следующем свойстве проекта
Linker->Input->Additional Dependencies
при создании набора DLL, которым во время компоновки требуется дополнительная информация, которая предоставляется файлами библиотеки импорта .LIB. В приведенном ниже примере, чтобы не получать ошибки компоновщика, мне нужно ссылаться на библиотеки DLL A, B, C и D через их файлы lib. (обратите внимание, чтобы компоновщик нашел эти файлы, вам может потребоваться указать путь их развертывания,Linker->General->Additional Library Directories
иначе вы получите ошибку сборки о невозможности найти какой-либо из предоставленных файлов lib.)Если ваше решение строит все динамические библиотеки, вы, возможно, смогли избежать этой явной спецификации зависимостей, полагаясь вместо этого на ссылочные флаги, отображаемые в
Common Properties->Framework and References
диалоговом окне. Похоже, что эти флаги автоматически выполняют связывание от вашего имени с использованием файлов * .lib.Однако это, как говорится, Общие свойства, которые не зависят от конфигурации или платформы. Если вам нужно поддерживать сценарий смешанной сборки, как в нашем приложении, у нас была конфигурация сборки для рендеринга статической сборки и специальная конфигурация, которая создавала ограниченную сборку подмножества сборок, которые были развернуты как динамические библиотеки. Я использовал
Use Library Dependency Inputs
иLink Library Dependencies
флаги установлены истину в различных случаях , чтобы получить вещи , чтобы построить , а потом реализовать , чтобы упростить вещи , но при введении кода для статической сборку я представил тонну компоновщик предупреждения и сборка была невероятно медленной для статических сборок. В итоге я ввел кучу подобных предупреждений ...warning LNK4006: "bool __cdecl XXX::YYY() already defined in CoreLibrary.lib(JSource.obj); second definition ignored D.lib(JSource.obj)
И я закончил использовать ручную спецификацию,
Additional Dependencies
чтобы удовлетворить компоновщик для динамических сборок, в то же время поддерживая статические сборщики, не используя общее свойство, которое их замедляло. Когда я развертываю сборку динамического подмножества, я развертываю только файлы dll, поскольку эти файлы lib используются только во время компоновки, а не во время выполнения.источник
Есть три вида библиотек: статические, общие и динамически загружаемые.
Статические библиотеки связываются с кодом на этапе компоновки, поэтому они фактически находятся в исполняемом файле, в отличие от общей библиотеки, которая имеет только заглушки (символы) для поиска в файле общей библиотеки, который загружается во время выполнения до вызывается основная функция.
Динамически загружаемые библиотеки очень похожи на разделяемые библиотеки, за исключением того, что они загружаются, когда и если в этом возникает необходимость из написанного вами кода.
источник
LoadLibrary()
и связанных API.Вот несколько связанных тем MSDN, чтобы ответить на мой вопрос:
Связывание исполняемого файла с DLL
Неявное связывание
Определение того, какой метод связывания использовать
Создание библиотеки импорта и файла экспорта
источник