Не так давно ув. тов. tyomitch дал ссылку на пример с planetsourcecode. Афтар примера делает следующее: подменяет LINK.EXE своим экзешником. Этот экзешник перехватывает, естественно, все вызовы (потому что афтар переименовал старый link в link1, а на его место свой под именем link), и меняет в параметрах командной строки ключики (подставляет ключ /DLL, изменяет точку входа и image base). Решение, конечно, хорошее, но есть несколько недостатков.
Во-первых, перехватываются все вызовы линкера. А мы что, только dll пишем? Конечно, нам exe тоже нужно создавать. А как псевдо-линкер узнает, что именно мы хотим? Афтар решил проблему просто: при каждом (при каждом) вызове линкера последний выводит окошечко – а не хотите ли, уважаемый, сделать dll. Лично меня это напрягает.
Во-вторых, линкеру требуется файл .def, в котором идёт перечень тех функций, которые нужно экспортировать. Для ведения таких файлов афтар сделал add-in. Он вам будет выводить листбокс, и вы там будете отмечать нужные функции. Связи между def и vbp никакой, поэтому, если вы ответите на вопрос псевдо-линкера "Да", то вам покажут диалог выбора файла def. Это меня тоже напрягает.
Суммарно: запрос на каждой компиляции любого проекта, необходимость ведения def, надстройка (вызывается вручную), выбор файлов def.
Мне это так не понравилось, что я решил сделать по-другому
Формат PE давно известен, потому сгенерить секцию экспорта относительно несложно. Главная проблема была в базовом адресе. У каждого исполняемого файла есть т.н. Image base, это виртуальный адрес, по которому необходимо загрузить программу. Если загрузить её в другое место, то перестанут работать все вызовы и все условные/безусловные переходы, которые есть в коде и которые были реализованы в виде absolute, а не relative инструкций процессора. Для того, чтобы преодолеть эту проблему, формат PE предусматривает секцию .reloc, которая содержит так называемую fix-up table. Эта таблица есть простой перечень тех мест в exe, где используется абсолютная адресация. Если файл придётся загрузить в место, отличное от image base, то загрузчик просто пробежится по всей этой таблице и во всех указанных в ней местах скорректирует адрес (добавит разницу между реальным адресом загрузки и image base).
Так вот, у исполняемых файлов, производимых VB, нет секции .reloc.
Это значит, что файл можно загрузить только по image base. Это нормально в том случае, когда exe запускается как новый процесс, но для библиотеки это совершенно недопустимо, ведь библиотека подгружается в адресное пространство другого процесса, и желаемый ею адрес может быть занят.
Кстати, отсутствие .reloc – это нарушение рекомендаций самого MS (и допустили это нарушение прогеры от MS):
MSDN писал(а):While it might be tempting to try and generalize the above and say "Relocations should be omitted from EXEs, but left in DLLs," there are counterexamples. For instance, let's say Program1 wants to read the resources from another EXE file (Program2). If Program2's relocations are missing, there's a good chance that Program1 won't be able to map Program2 into memory to access its resources. A real-world example could be your program, which has an icon for its main window. To show your program's icon, EXPLORER or PROGMAN needs to be able to load your file to access the icon resources.
За эту цитату спасибо tyomitch.
Вот я и подумал...
Добавить .reloc в exe после компиляции – это я не потяну (да и никто, думаю...). Но вот скормить ключ /FIXED:NO линкеру можно завсегда. Тем же способом, что использовал афтар с плэнетсурскоуд. Только отличие в том, что вам не придётся отвечать ни на какие вопросы – просто во всех exe начнёт появляться секция .reloc. Вы её и не заметите. Размер файла увеличится незначительно, а возможности резко возрастут. В частности, можно будет грузить exe через LoadLibrary, чтобы, например, ресурсы использовать ейные. Красота
А как же .edata? А мы её ручками, я ж говорил
Система работает так. Берём прилагаемый pseudo-linker, копируем его в каталог с VB (а старый линкер предварительно обзываем link_old.exe). Потом копируем файлы Native Dll.vbp, modConversion.bas и modLibrary.bas в папку с шаблонами проектов. Запускаем среду. Выбираем проект Native Dll. Там будет два модуля, в modConversion что-то менять не рекомендуется. Идём в modLibrary, создаём функции, для каждой экспортируемой функции заводим команду Exports (это я на манер Дельфи решил сделать ). Компилируем. Получаем... Нет, не dll. Получаем обычный exe. А вот запускаем этот exe – и появляется dll. Рулез
Важное замечание. Можно использовать все функции VB, которые объявлены в модулях. А вот объекты среды VB использовать нельзя, потому что инициализация виртуальной машины не производится! То есть можно поюзать функцию Left$, к примеру. А вот Screen.Width поюзать нельзя, потому что нет такого объекта. Утешает лишь то, что, во-первых, у означенного первым афтара проблема та же самая, а во-вторых, все эти Screen прекрасно можно через API. Declare function, разумеется, тоже допускается.
Интересно, tyomitch возьмётся попробовать внедрить инициализацию виртуальной машины в точку входа библиотеки?