Связывание с файлом 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 /list xxx.lib
иlink /dump /linkermember xxx.lib
. См. Этот вопрос о переполнении стека .