Неразрешенный внешний символ при создании DLL - PullRequest
4 голосов
/ 31 марта 2012

Мой друг получает кучу ошибок при создании DLL. Visual Studio жалуется на неразрешенные внешние символы. Я в основном пользователь Unix, поэтому я могу ошибаться там. В Unix, когда вы создаете статическую библиотеку (архив), она делает не больше, чем объединяет различные объектные файлы в архивный файл. Я ожидаю, что динамические объекты будут создаваться таким же образом, но, очевидно, имеет место дополнительный этап связывания.

Первый вопрос: Почему существует стадия связи для dll?

В этом случае библиотека DLL содержит неопределенные символы, поскольку мы ожидаем, что DLL найдет эти символы в файле EXE. Это совершенно противоположно типичному поведению DLL, где EXE использует символы, определенные в DLL. Чтобы прояснить ситуацию, я ожидаю, что эти символы будут найдены прямо при загрузке DLL в память.

Второй вопрос: Как заставить библиотеку DLL использовать символы, определенные в файле EXE?

EDIT: Я переформулировал вопрос, так как думаю, что недостаточно четко сформулировал проблему.

Ответы [ 2 ]

11 голосов
/ 31 марта 2012

Вы описали происхождение вашей проблемы следующим образом: «Я хочу, чтобы DLL импортировала некоторые символы из exe-файла» в комментарии к ответу Лучиана Григоре.Вы дополнительно написали в тексте своего вопроса, что хотите, чтобы «DLL находила эти символы в EXE-файле. Мы ожидаем, что DLL найдет эти символы в EXE-файле».

В основном это вопрос разработки, следует лиэкспортировать функции или данные из exe или нет.Обычно каждый создает экспорт только из DLL.Если EXE необходимо предоставить некоторую информацию в DLL, она предоставляет информацию по параметрам .Например, вы вызываете в EXE какую-то функцию MyFunc, реализованную и экспортированную в DLL.В качестве дополнительного параметра MyFunc вы получаете указатель context, который может напрямую или косвенно получить DLL-библиотеку всей необходимой информации для EXE.

В некоторых редких ситуациях вы можете экспортировать данные или функции из EXE.Например, вы используете утилиту DumpBin.exe (просто запустите «Командная строка Visual Studio (2010)», чтобы использовать ее), чтобы убедиться, что Outlook.exe экспортирует

DumpBin.exe /exports "C:\Program Files\Microsoft Office\Office14\OUTLOOK.EXE"

File Type: EXECUTABLE IMAGE

  Section contains the following exports for outlook.exe

    00000000 characteristics
    4E79B6C8 time date stamp Wed Sep 21 12:04:56 2011
        0.00 version
           1 ordinal base
          66 number of functions
          66 number of names

    ordinal hint RVA      name

          1    0 00B58A88 CleanupAddressComponents
          2    1 00B58A88 CleanupNameComponents
          3    2 00228DC4 DllCanUnloadNow
          4    3 004848F8 DllGetClassObject
          ...
         65   40 0038EF30 UpdateContactTracker
         66   41 00902788 dwIsLoggingEnabled

Я могу объяснить, как вы можете реализовать сценарийбез долгих обсуждений, когда и нужно ли вам это делать.

Прежде всего, файл LIB содержит файлы OBJ, которые имеют другой формат: Исполняемый файл программы (PE) .Во время компиляции в общий файл будет помещен другой общий раздел.Очень важно, что исполняемый файл программы (EXE или DLL) содержит не только код, но и много дополнительной информации в заголовочной части PE.Наиболее важными являются

  • Экспорт каталога
  • Импорт каталога
  • Импорт каталога таблиц адресов
  • Базовый каталог перемещения
  • Каталог ресурсов

Вы можете использовать утилиту DumpBin.exe (просто запустите «Командная строка Visual Studio (2010)», чтобы легко ее использовать).Для просмотра информации о заголовках вы можете использовать DumpBin.exe /headers my.exe.Для просмотра содержимого каталога экспорта вы можете использовать DumpBin.exe /exports my.exe и т. Д.

Если вы компилируете DLL, которая экспортирует некоторые функции или данные, файл LIB будет создан дополнительно.Это так называется библиотека импорта .Если вы используете LIB в вашем проекте EXE, который использует некоторые функции или данные из DLL, компоновщик разрешит внешние ссылки и поместит в каталог импорта EXE информацию о функциях, которые должны быть разрешены во время загрузки..

Таким образом, библиотека импорта содержит только шаблоны для заполнения каталога каталогов импорта и импорта таблицы адресов в EXE.

В общем случае аналогичным образом можно экспортировать некоторые данные функций из EXE, создатьLIB, использует LIB в проекте DLL и таким образом реализует импорт некоторой информации в DLL из EXE.

Я сделал демонстрационный проект , который демонстрирует путь. Пожалуйста, внимательно прочитайте инструкции по компиляции в конце моего ответа, если хотите удалить все LIB из Проекта и создать все самостоятельно .Код ExportFromExe.c (EXE):

//#define CREATE_IMPORT_LIBRARY_ONLY
#include <Windows.h>

EXTERN_C __declspec(dllexport) int someData = 0;
EXTERN_C __declspec(dllexport) int __stdcall myFunc (int x);
EXTERN_C __declspec(dllexport) int __stdcall MyFunc();

int __stdcall myFunc (int x)
{
    return x + 10;
}

#ifndef _DEBUG
int mainCRTStartup()
#else
int main()
#endif
{
    someData = 5;
#ifndef CREATE_IMPORT_LIBRARY_ONLY
    return MyFunc();
#endif
}

Код MyDll.c (DLL):

#include <Windows.h>

EXTERN_C __declspec(dllexport) int myData = 3;
EXTERN_C __declspec(dllimport) int someData;
EXTERN_C __declspec(dllimport) int __stdcall myFunc (int x);

#ifndef _DEBUG
EXTERN_C BOOL WINAPI _DllMainCRTStartup (HINSTANCE hinstDLL, DWORD fdwReason,
                                         LPVOID lpvReserved)
#else
BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
#endif
{
    if (fdwReason == DLL_PROCESS_ATTACH)
        DisableThreadLibraryCalls(hinstDLL);

    return TRUE;
    UNREFERENCED_PARAMETER (lpvReserved);
}

EXTERN_C __declspec(dllexport) int WINAPI MyFunc()
{
    return someData + myFunc(myData);
}

Для возможности успешного создания проекта наВ первый раз мы должны решить проблему: «кто был первым: курица или яйцо?»потому что проект EXE зависит от MyDll.lib, а проект DLL зависит от ExportFromExe.lib.Для первой компиляции EXE мы можем временно удалить $(OutDir)MyDll.lib из настройки компоновщика проекта EXE и определить CREATE_IMPORT_LIBRARY_ONLY.В результате мы создадим ExportFromExe.exe и ExportFromExe.lib.В более крупных проектах вместо этого можно использовать опцию компоновщика Undefined Symbol Only (/FORCE:UNRESOLVED).Затем мы можем построить MyDll проект, который создает MyDll.dll и MyDll.lib.Теперь вы можете удалить CREATE_IMPORT_LIBRARY_ONLY из EXE и включить $(OutDir)MyDll.lib в качестве настройки компоновщика («Дополнительные Depandencies» в разделе «Input» настроек).Следующая сборка проекта EXE даст окончательное решение.

Я использовал несколько небольших хитростей для удаления C-Runtime и уменьшения размера EXE и DLL до 2,5 или 3 КБ.Таким образом, вы можете использовать /all переключатель DumpBin.exe для проверки полной информации из двоичных данных EXE и DLL включительно.

Поскольку EXE возвращает результаты как ERRORLEVEL, вы можете протестировать приложение в командной строке:

echo %ERRORLEVEL%
0

ExportFromExe.exe

echo %ERRORLEVEL%
18
2 голосов
/ 31 марта 2012

Первый вопрос: почему для DLL существует стадия связи?

Потому что так оно и есть. Каждый раз, когда вы хотите создать двоичный файл, существует этап связывания. Символы должны быть как-то разрешены, верно?

Второй вопрос: как я могу это сделать?

Вы добавляете файл lib, который создается вместе с dll, к Additional Dependencies из вашего проекта - Свойства -> Свойства конфигурации -> Компоновщик -> Ввод

Примечание:

Если вы этого еще не сделали, чтобы экспортироваться в lib, символы должны быть объявлены с _declspec(dllexport). Когда вы включаете заголовки, вы сообщаете компилятору, что эти символы должны быть импортированы с _declspec(dllimport).

...