Delphi programming blog
Источник: http://teran.karelia.pro/articles/item_4448.html
 

Проблема с получением интерфейса из dll

Опубликовано 09.12.2010 г. 17:27

решил реализовать вроде как простую вещь: есть некоторый интерфейс, класс его поддерживающий, реализован в библиотеке. Есть функция экспорта, создает объект класса, возвращает ссылку на интерфейс. Главное приложение используя функцию библиотеки получает ссылку на интерфейс. Поскольку опыта в написании таких вещей нет, то ничего видимо и не работает (: как сделать?

Алгоритм следующий:
  1. Создаем новый проект- VCL Forms приложение testProject.exe.
  2. Добавляем в группу проектов новую DLL библиотеку – testLib.dll.
  3. В проект библиотеки добавляем новый модуль – testIntf.pas, в интерфейсной части которого описываем требуемый пустой интерфейс ITestInterface.
    type
        ITestInterface = interface
        end;
  4. В главный файл библиотеки добавляем описание класса TTestObject, предка TInterfacedObject, поддерживающего интерфейс ITestInterface
    library testLib;
    uses
      testIntf in 'testIntf.pas';
    {$R *.res}
    
    type
        TTestObject = class(TInterfacedObject, ITestInterface)
        end;
  5. Здесь же добавляем функцию для создания объекта, с возвратом указателя на интерфейс. Модель вызова safecall.
        function ExportObject():ITestInterface; safecall;
        begin
            result := TTestObject.Create();
        end;
  6. Добавляем имя функции в список экспортируемых.
    exports
        ExportObject;
  7. Сохраняем всю группу проектов.
  8. К проекту testProject подключаем файл описания интерфейса testIntd.pas
  9. Определяем сигнатуру экспортируемой функции
    var getObject : function(): ITestInterface; safecall;
  10. описываем необходимые переменные
        x : ITestInterface;
        hLib : THandle;
  11. Загружаем библиотеку.
        hLib := LoadLibrary('testLib.dll');
  12. Получаем точку входа в функцию экспорта объекта.
        @getObject := GetProcAddress(hLib,'ExportObject');
  13. Вызываем функцию, получаем ссылку на интерфейс
        x := getObject();
  14. тут инициализируется объект Application, работает приложение, и закрывается.
        Application.Initialize;
        Application.MainFormOnTaskbar := True;
        Application.CreateForm(TForm1, Form1);
        Application.Run;
  15. Обнуляем ссылку на интерфейс
        x := nil;
  16. Выгружаем библиотеку.
        FreeLibrary(hLib);
Результат: при обнулении ссылки, refCount = 2, уменьшается на 1, и остается равным 1 соответственно. При этом выгрузка библиотеки падает. Вопрос: где я не прав и почему такое происходит? Eсли в вызывающей программе изменить модель вызова на stdcall:
var getObject : function(var x : ITestInterface):HResult; stdcall;
...
    getObject(x);
то все работает. внимание вопрос: почему в появляется дополнительная ссылка на интерфейс? мозг на грани катастрофы. подскажите кто-нибудь? :)
Метки:  интерфейсы 

Комментарии

smile
09.12.2010 в 22:13
Потому что возвращаешь в результате (Result) функции.
Оно при выходе из функции 1,
при присваивании переменной +1. 1+1=2
Переменную убил -1

var
src: ISourceDataset;

Типовой пример, как надо:
function GetSourceDataset(out Dataset: ISourceDataset): Boolean;
...
if not GetSourceDataset(src) then
Assert(False, 'ошибка');

Ну или не зависимо от pascal:
function GetSourceDataset(out Dataset: ISourceDataset): hResult;
...
if not Succeeded(GetSourceDataset(src)) then
Assert(False, 'ошибка');
или
...
OleCheck(GetSourceDataset(src));
ter
09.12.2010 в 23:29
дак у резалта функции область жизни не ограничивается что ли самой функцией? вернули результат и все.
function GetSourceDataset(out Dataset: ISourceDataset): hResult; stdcall;

соответствует
function getSourceDataSet():ISourceDataSet; safecall;

или вы хотите сказать что через safecall нельзя интерфейсы возвращать чтоли? да и вобще как результат любой функции.
Алексей
12.12.2010 в 23:40
Вот тут есть серия статей про написание плагинов с использованием интерфейсов:
http://www.gunsmoker.ru/2008/12/1.html

Надеюсь, поможет.
smile
13.12.2010 в 11:11

дак у резалта функции область жизни не ограничивается что ли самой функцией


Да, иначе из функции вообще ничего не вернуть, тк если по выходу будет RefCount=0 => Free.
ter
13.12.2010 в 11:31
Вот тут есть серия статей про написание плагинов с использованием интерфейсов:
http://www.gunsmoker.ru/2008/12/1.html
читал уже (: жаль цикл не дописан до конца (:

Да, иначе из функции вообще ничего не вернуть, тк если по выходу будет RefCount=0 => Free
мне что то казалось, что результат функции живет не до конца end'а вызывающей подпрограммы, а как то уничтожается сразу по возврату результата. Иными словами мы имеем неявную ссылку на интерфейс, если смотреть со стороны вызывающей программы.
Riff
05.01.2011 в 21:00
Извини, если второй раз посылаю это сообщение, но после первой отправки не было показано никакого подтверждения...

Вот так сработает:

var
i: IInterface;
h: Cardinal;
a: function: IInterface; safecall;

procedure a1();
begin
h := LoadLibrary('project2.dll');
a := windows.GetProcAddress(h, 'a');
i := a();
end;

procedure a2();
begin
end;

a1; //пункт 14 в твоём сообщении
//пункт 15
i := nil; //пункт 16
FreeLibrary(h);

т.е. вынесением пункта 14 в отдельную процедуру. Иначе текущий контекст рассматривается как одна сплошная процедура и освобождение памяти (сброс счётчика у интерфейса) не происходит до выхода из неё.
ter
09.01.2011 в 19:03
ну да. это уже пробовал (: с stdcall проше (:
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно