![]() |
|
![]() ![]() ![]() |
|
Cr@$h |
|
||||||||||||||||||||||||||||
![]() Исследователь ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1693 Регистрация: 3.4.2005 Где: Санкт-Петербург, Россия Репутация: 1 Всего: 41 |
Эта тема посвящена специальному вопросу коллизии имёен процедур при подключении в Fortran нескольких DLL с одинаково названными процедурами. Она выделилась из темы "Вызов DLL из программы на фортране", в которой рассмотрены основы подключения без сингулярных случаев.
Постановка задачи Итак, стоит следующая проблема. Проект на Fortran использует несколько DLL, в которых есть процедуры с одинаковыми именами. Как указать, из какой именно библиотеки используется процедура, имя которой присутствует сразу в нескольких?. ![]() ![]() Мне видится пока три варианта решения вопроса, используя как явное, так и неявное подключения. ![]() ![]() Изначально имеем две библиотеки: DLL1 и DLL2. DLL1
DLL2
Вся заурядность их видна сразу: обе называют свою единственную процедуру одним и тем же именем ImportedProcedure. Ну что тут будешь делать. ![]() ![]() Назначение директив Запомним сразу:
1. Разрешение коллизии имён при явном (динамическом) подключении DLL При явном подключении используются специальные процедуры Win API. Вначале вызывается функция LoadLibrary, чтобы загрузить DLL, затем используется функция GetProcAddress, чтобы получить указатели на требуемые функции (или переменные), а по окончании работы с ними вызывается FreeLibrary, чтобы выгрузить библиотеку и освободить занимаемые ею ресурсы. Надеюсь, код достаточно документирован и всё будет понятно. Признаюсь, сейчас я сам первый раз использую явное подключение. При использовании Win API нужно указать use kernel32. 1.1. Использование одной из двух процедур Хорошо. Используем две библиотеки: DLL1 и DLL2. Про них уже знаем, что обе содержат процедуру с именем ImportedProcedure. Ладно, чёрт с ним, хотим вызвать хотя бы обну из них, но чтобы точно указать какую именно. ![]() Идея проста: в процедуре GetProcAddress при связи адреса интерфейсной процедуры с истинным адресом загруженной процедуры и будем решать, из какой библиотеки её брать. Действуя так, сначала вызовем из первой DLL, а потом из второй, и всё под одним и тем же именем! Т.е. в исходном тексте программы будет объявлена всего одна внешняя (интерфейсная) процедура ImportedProcedure. Вот код, процедуры, в которой это реализовано:
Последнние комментарии подытожили. В результате имеем то, что имеем:
Задача при явном подключении решена: удалось вызвать процедуры с одинаковым именем из двух разных библиотек, да ещё и указать когда какую. 1.2. Использование двух процедур сразу Теперь хотим использовать две процедуры независимо одна от другой -- т.е. иметь в исходном коде две процедуры: одна из одной DLL, другая -- из другой. Нет проблем. ![]() Процедурка, реализующая это подключение:
Всё работает правильно:
Вторая задача при явном подключении решена: удалось вызвать процедуры независимо друго от друга, представив их разными внешними процедурами, хотя в своих DLL они имеют одно и то же имя. 2. Разрешение коллизии имён при неявном (статическом) подключении DLL Хорошо. Мы порезвились на славу. Собственно, при явном подключении и делать было нечего: всю гибкость этого дела задавала функция GetProcAddress, в ней мы указывали, из какой библиотеки брать процедуру. А дальше дело техники: заводить ли одну процедуру и её целочисленный указатель, указывая им на определённую библиотечную процедуру, или заводить по принципу "каждой твари по паре": своя пара процедура--указатель для каждой DLL. При неявном подключении такого уже не проделать. Тут меньше гибкости, равно как и геморроя. Всё, что нужно, это подключить к проекту соотв. файл статической библиотека. Как сделать это, в частности, и использовать неявное подключение, вообще, я описал здесь. 2.1. Задание интерфейса внешней процедуры Самое ответственное здесь -- задание интерфейса внешней процедуры. К обоим процедурам DLL это делается одинаковым образом, т.к. они имеют одно и то же имя.
Но из-за их одинаковости компилятор и не будет знать, из какой именно DLL брать ImportedProcedure. 2.2. Два одинаковых интерфейса. Какую библиотеку предпочтёт компилятор? Хорошо. Поступим для эксперимента, как можем: задаим для двух внешних процедур одинаковый интерфейс и посмотрим из какой DLL будет грузить компилятор. Вот процедурка:
Оба интерфейса задают обращение к одной и той же внешней процедуре ImportedProcedure. При этом к проекту подключаются статически через .lib обе DLL (через Project Dependencies...). Проект компилируется и собирается без ошибок. Из какой же DLL будет грузить процедуру компилятор? Смотрим:
Выбрал первую DLL! Она дважды вывела строки. Ну, его понять можно -- любовь с первого взгляда ![]() ![]() 2.3. Гипотетический атрибут DLLNAME, и дело в шляпе. К сожалению, разработчики почему-то не предусмотрели директиву типа DLLNAME, которая бы позволяла указать, из какой именно DLL экспортируется данная процедура. Вот гипотетический пример такого интерфейсного объявления.
Благодаря этому, можно было бы объявить две разные внешние процедуры, которые представляются в своих DLL одинаковым именем. Но так делать незя. ![]() 2.4. Ну, а если по делу? ![]() Единственным выходом здесь вижу использование, а точнее редактирование, .lib файла -- статической библиотеки импорта у каждой DLL. Её следует изменить так, чтобы в каждой заменить имя в таблице ImportedLibrary на ImportedLibrary1 и ImportedLibrary2, соответственно для каэждой из библиотек. Сами DLL менять не будут, просто статическая библиотека будет разруливать ImportedLibrary1 в ImportedLibrary от DLL1, а другая ImportedLibrary2 тоже в ImportedLibrary от DLL2. Это можно попробовать сделать с помощью LIB.exe. Возможно, потребуется .obj файл, т.к. на входе она требует: объектные файлы COFF, 32-битные объектные файлы OMF или существующие COFF библиотеки. Опять же смотрим первую ссылку абзаца. Как экспортировать из DLL, используя файл экспорта .def, который задаётся вручную, описано здесь. Это уже высший пилотаж менять .lib без исходных кодов. Возможно, я разберусь ещё с этим или мне помогут. Для неявного связывания ставлю здесь... Наверное, это один из редких случаев, когда неявное связывание проигрывает в гибкости явному, но советую читать дальше. 3. Разрешение коллизии имён при наличии исходного кода обеих DLL Более административный метод. Вообще, это не очень хорошо иметь библиотеки, содержащие одинаковые процедуры, если они чем-то различаются, то это лучше отразить в их именах. Оба следующих метода не зависят от типа подключения, т.к. меняют имена в самих DLL. 3.1. Переименовывание процедур в исходном тексте Изменение имён можно сделать вручную. Вот как будут выглядеть библиотеки. DLL1
DLL2
Т.е. мы сознательно сами дали процедурам разные имена: ImportedProcedure1 и ImportedProcedure2. Код проекта приводить не имеет смысла, т.к. сама коллизия теперь не имеет места: две библиотеки имеют по-разному названные процедуры. Их теперь можно вызывать по их уникальным именам и никто ругаться на нас не будет. 3.2. Задание разных псевдонимов процедур в исходном тектсе Это более гибкое, решение, чем первое. Раз уж мы итак используем псевдонимы (ALIAS), чтобы учесть регистр букв, то почему бы в нём же не задать номер процедуры -- её единственное отличие в имени. DLL1
DLL2
Здесь всего два изменения: ALIAS: "ImportedProcedure1" в первой и ALIAS: "ImportedProcedure2" во второй процедурах. Эффект будет тот же, но при желании всё можно будет быстрее вернуть на прежние места. В первом случае псевдонимы "совпадали" с именами в исходном тектсе: ImportedProcedure1 для IMPORTEDPROCEDURE1 и ImportedProcedure2 для IMPORTEDPROCEDURE2. На этот раз мы изменили только псевдонимы -- они отличаются от имён в исходных кодах библиотек, и под ними процедуры и будут расположены в DLL'ах: ImportedProcedure1 и ImportedProcedure2 для имени в исходном тектсе IMPORTEDPROCEDURE у обоих процедур. Заключение Ну что. ![]() Можно ожидать новых директив от разработчиков, т.е. у моря погоды. Если имеются исходные коды библиотек, то неплохо бы изменить имена процедур в них, либо вручную, либо, задавая свои уникальные псевдонимы процедурам. Думайте об организации проекта. Если исходных текстов нет или вы не хотите их менять, но хотите использовать статическое подключение, то могу предложить начать изучать LIB.exe. В принципе это реально. Нужно будет написать свой .def файл с информацией об экспоритуемых процедурах. Похоже понадобится не только .lib и .dll файлы, но и .obj файл библиотеки. Это ещё не исходный код, но объектные файлы не всегда идут с библиотеками. Сам иду отдыхать, сидел всю ночь, и до конца в MSDN этот вопрос изучить не успел. Кто добьёт -- репа ++. P.S. Я прикрепляю решение, в котором сегодня это дело исследовал. Для проекта статического подключения (неявного) зависимости (dependency) уже установлены. Если собираетесь менять DLL'ки не забудьте их скопировать из папки debug в папки debug других двух проектов. Удачи. Это сообщение отредактировал(а) Cr@$h - 13.7.2006, 07:21 Присоединённый файл ( Кол-во скачиваний: 6 ) ![]() |
||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
Karabas |
|
|||
Новичок Профиль Группа: Участник Сообщений: 18 Регистрация: 14.6.2006 Репутация: нет Всего: нет |
Наверное, зависит от компилятора, но мой power station 4 не хочет собирать окончательный проект.
Я создал консольное приложение, добавил use msfwin, а остальное копи-паст из файла ImplicitLinking. И заменил !DEC$ATTRIBUTE на !MS$ATTRIBUTE . во-первых, ругается на строчку pointer( ptrImportedProcedure, ImportedProcedure) Error: pointer-based variable IMPORTEDPROCEDURE of INTEGER POINTER can't be a function call А если переделать этот поинтер в просто integer то выдает: unresolved external symbol _IMPORTEDPROCEDURE@4 Даже подключение к проекту библиотек .lib не помогает (я скомпилил заново обе длл-ки тоже) Или может быть моя версия фортрана совсем старая? ![]() |
|||
|
||||
Cr@$h |
|
||||||||||||||||
![]() Исследователь ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1693 Регистрация: 3.4.2005 Где: Санкт-Петербург, Россия Репутация: 1 Всего: 41 |
Погоди. Это же в файле ExplicitLinking, а не ImplicitLinking ![]()
Похоже, он говорит, что нельзя ссылаться на процедуру. Попробуй поменять их местами, но это на вряд ли поможет.
Они в Implicit (неявном) проекте должны использоваться, там должно всё работать. Можешь тоже нижнее подчёркивание использовать:
Это может только по части указателей (Cray) -- это расширение над стандартом, но думаю, всё же должно как-то работать. Другое дело, он может игнорировать некоторые атрибуты. HELP можешь выслать, если есть? Можно покапаться посмотреть специфику.
Попробуй нижний пробел поставить:
или даже так
Но тоже не уверен. Просто эта ошибка сигнализирует о том, что внешняя процедура ImportedProcedure не подключилась. Что-то с работой указателей. Погоди, саму ImportedProcedure объявлять как integer не надо. Это внешняя процедура, к которой задан интерфейс. |
||||||||||||||||
|
|||||||||||||||||
![]() ![]() ![]() |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Fortran | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |