Прошу критики описанного ниже подхода работы с DLL.
DLL экспортирует некоторую функцию, которая в var параметре возвращает указатель, который на самом деле является указателем на экземпляр класса в DLL.
А потом другие функции получают его, как параметр.
В вызывающем коде этот указатель нигде не используется, как объект из DLL, а только для передачи в другие функции, которые тоже реализованы в DLL.
Я понимаю, что так не делают, но код не мой. Конечно, надо скрыть этот указатель внутри DLL, и передавать в функциях неявно. Но код работает и хочу обсудить опасность такого подхода.
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
24-03-2019 23:56 | Сообщение от автора вопроса
Да, конечно. Это хорошо видно, когда используешь интерфейсы в СИ++. Можно абстрактным классом объявить, а можно и структурой. Я просто сейчас подумал, что и на Pascal ничто не мешает поступать так. А то как-то читал множество обсуждений по теме "чистые интерфейсы в Delphi".
Указатель впоне себе сойдет за хендл или кук, и его передача ничем не лучше и не хуже передачи любого абстактного числа. А напортачить можно с чем угодно, в том числе и хендлом файла, например. Волшебной палочки все равно не существует.
А интерфейс - это и есть таблица с адресами методов. Точнее, адрес такой таблицы. Хоть СОМ, хоть какой
Конечно, никто не предлагает передавать экземпляры классов туда-сюда "без всякого страху". А а данном же случае, я уже понял, что указатель (представленный обычным целочисленным значением, к тому же), без информации, что он есть ссылка на некий объект, есть обычный handle или instance. Он просто возвращается одной функцией и заворачивается обратно в DLL параметром для других функций. Но мой коллега, "испугавшись" моих замечаний, уже всё переделал.
Кстати, эта тема родила у меня мысли по поводу реализации обычных (не COMовских) интерфейсов в Delphi. Хочу попробовать делать это обычной структурой с полями процедурного типа. Сейчас, кстати, рекорды стали, почти, тем же, что и Object. А Object - более близок к СИшным классам, без обязательного корневого объекта.
Ну, если я, конечно, не заблуждаюсь. :)
До тех пор, пока вы пользуетесь одинаковыми версиями Delphi для сборки DLL и головного приложения - никаких проблем нет (ну, кроме разве что уже указанной ранее "попытка несанкционированного удаления"). Но как только версии Delphi будут разными - всё будет сильно зависеть от того, как звёзды сложатся. Ведь фишка Delphi в том, что он на самом деле вызывает НЕ метод PrintPony, а "нечто, расположенное по смещению адрес объекта +104" (например). А для другой версии в этой области памяти находится метод KillAllHumans, потому что у той Delphi в TObject почему-то было больше (или меньше) методов и таблица "поехала". Надеюсь, не нужно объяснять, чем чреват вызов KillAllHumans вместо PrintPony?
Да, библиотеку планируют поставлять всем подряд. Коллега обещал переписать и скрыть свой указатель. В принципе его сложно использовать не по назначению, потому что это просто некое целочисленное значение с именем что-то типа instance. При его получении в библиотеке создаётся объект (не знаю, как он его освобождает) и посредством этого значения передаётся всем библиотечным функциям. Т.е. одни и те же функции работают с разными экземплярами класса. На мой взгляд тут просто просятся интерфейсы.
>>>Я понимаю, что так не делают, но код не мой ... хочу обсудить опасность такого подхода
Бытует мнение, что степень опасности использования нестандартного подхода, прямо пропорциональна расстоянию от места расположения самого разработчика этого подхода.
Пока вы помните, как нужно использовать и используете сами или в своей среде - это одно и вполне можно использовать всякое. В этом случае, подход вашего коллеги вполне стандартный. Другое дело, если же вы делаете api или dll "на заказ", то нужно быть готовым, что документацию читать будут, в лучшем случае, поверхностно. Поэтому, всё зависит от того, с какой целью вы пишите dll и в какой среде она будет использоваться.
>>>Лучшим было бы использовать COM-интерфейсы. Но коллега поставил себе задачу писать кроссплатформенный код.
По поводу кросс-платформенности. По-моему, чем кросcплатформеннее, тем неповоротливее. Причём, как в плане разработки, так и в отношении скорости выполнения. Но зато эстетично, красиво, современно.
Как пример, api БД Firebird. Разработчики запилили, для версии 3.0 новый, объектно-ориентированный api. На интерфейсах, языко-независимый, платформо-независимый, windows-ole-независимый. Сказка, а не api. http://www.ibase.ru/files/firebird/fbapi.html#_fbapi_interfaces_iprovider
Только почему-то, пока все, старым, процедурным api пользуются (и Fib-ы, и Dac-и, и Ibx-ы).
>>>Она благополучно проработала много лет
В использовании объекта как таковом вроде бы нет проблем - они возникают при путанице в работе двух менеджеров памяти - клиента и DLL. Или если обе стороны скомпилированы с несовместимыми настройками. То есть в Вашем частном случае все было хорошо, но на это нет гарантий
Спасибо. Я тоже, вроде, не вижу явной опасности, но все таки посоветовал коллеге использовать другой идентификатор объекта, какой-нибудь id, например.
В принципе, это уже и есть некий хендл, так как представляет собой целое значение. Обратиться к полям и методам экземпляра можно только методом хака.
Кстати, когда я был молод и наивен, то написал библиотеку, где передавал TStream. Она благополучно проработала много лет. Или память так легла при компиляции, или дергал "безопасные" методы. :)
Лучшим было бы использовать COM-интерфейсы. Но коллега поставил себе задачу писать кроссплатформенный код. Хотя ядро активно использует WinAPI. Тут я ещё буду его переубеждать. Но в ходе наших споров у меня появилась очередная идея реализации чистого (не COM) интерфейса. Думаю реализовать его как структуру с полями процедурного типа. К сожалению, нет времени для опытов. :)
Опасность стандартная - попытка удалить память по указателю или воспользоваться методами объекта. Некритично, на мой взгляд, но правильнее было бы заменить хэндлом
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.