2009-07-25 4 views
3

Я пишу приложение на C, которое может быть расширено во время выполнения с помощью модулей/общих объектов/библиотек DLL. Эти модули могут использовать API существующей программы, но могут также предоставлять новые функции для использования в более поздних загружаемых модулях, поэтому существует вероятность того, что модули будут иметь зависимости друг от друга.Динамические модули с DLL на Windows

Мой текущий подход под Linux заключается в том, чтобы каждый модуль определял функцию depend(), которая возвращает список других имен модулей, от которых это зависит. Таким образом, я могу скомпилировать и связать каждый модуль для себя, загрузить модуль с помощью dlopen() и RTLD_LAZY, сначала разрешить его зависимости, а затем полностью загрузить его с помощью RTLD_GLOBAL. Это прекрасно работает и делает именно то, что я хочу. Он также позволяет мне заменить модуль на другую версию без перекомпиляции всех других модулей в зависимости от нее.

Актуальная проблема возникает при переносе на Windows. Во-первых, я не нашел способа связать DLL, не предоставив ей таблицы символов экспорта всех его зависимостей. Есть ли тот, который я забыл?

Во-вторых, LoadLibraryEx из Windows API, похоже, не может выполнять какую-либо ленивую загрузку, потому что вместо того, чтобы позволить мне обрабатывать зависимости, он идет вперед и загружает все связанные DLL-файлы, прежде чем он вернется. Поскольку я хотел бы также проверить версию, прежде чем загружать модули в будущем, это совсем не то, что я хочу. Есть ли способ обойти это поведение?

Третья особенность заключается в том, что я не могу заменить DLL, не перекомпилируя все остальные модули в зависимости от нее. На самом деле это иногда срабатывает, но обычно происходят дикие вещи или программа segfaults.

Можно ли даже написать такое модульное приложение в Windows? Любые предложения или разные подходы заслуживают высокой оценки!

Обновление: Просто чтобы пояснить, как мои модули используют функции друг друга в Linux (что я хотел бы иметь и в Windows): каждый модуль просто возвращает имя другого модуля, который он хотел бы вызвать выполняет функции из описанной функции depend() и включает ее заголовок, а затем вызывает используемые функции непосредственно в коде без какой-либо обертки. Это работает, потому что Linux не требует, чтобы все символы были разрешены во время соединения для общих объектов.

ответ

4

Вы можете экспортировать все функции вручную (используя __declspec(dllexport)) и загрузить их с помощью GetProcAddress. В этом случае вам нужно знать подпись каждой функции, и вы ограничены только функциями C, но это будет работать. Если вы скомпилируете оба модуля, ваши C-функции также могут возвращать классы C++, подробнее об этом позже. Использование GetProcAddress & LoadLibrary делает модули полностью независимыми. В принципе, вы делаете ссылку вручную, но, как я понимаю, это то, что вы делаете в Linux, не так ли?

LoadLibary загружает библиотеки, на которые зависит библиотека, поэтому убедитесь, что они не загружаются, зависят друг от друга. Либо они действительно независимы, либо они не являются, если все сделано правильно, изменение одной библиотеки не приведет к перекомпиляции другой (поскольку вы не связываете их вместе).

Хорошая идея - использовать что-то вроде COM, поэтому каждый из ваших библиотек возвращает интерфейс, а не отдельные функции. Таким образом, вы можете просто загрузить всю DLL, а также легко связать их (передача DLL -> передача объекта). Посмотрите XPCOM и COM, на самом деле это очень легко сделать.

+1

Хорошо, ваше предложение о 'GetProcAddress' уже немного помогает. Я просто попробовал это и назвал функцию из DLL таким образом, не связавшись с ней.Но это означало бы, что я не мог просто позволить модулю включать заголовки того, от кого он зависит, как в Linux, не так ли? Мне нужно как-то собрать все необходимые указатели функций с помощью 'GetProcAddress', прежде чем вызывать что-либо из родительского модуля. Спасибо также за отзыв с COM, я посмотрю на это. – smf68

+1

Да, но, как я уже сказал, просто сгруппируйте все функции для одного модуля в класс и вызовите это. Это намного проще, и это также уменьшает количество экспортированных символов. Это не COM, поскольку у вас нет наследования, это просто основано на аналогичной идее. Ваши клиенты используют интерфейс, не зная фактической реализации за ним. – Anteru

+0

Интересная тема. Таким образом, нет никакого способа сделать это без того, что разработчик модуля должен иметь знания о WinAPI и/или COM? :-( – Gregor