Созданный COM-компонент становится недействительным после выхода из метода (но не в область его действия)
В настоящее время я тестирую два внешних компонента COM. У меня большая проблема с одним из них, но я не могу найти причину такого поведения. Позвольте мне привести пример.
const
CLASS_SomeClas: TGUID = '{SomeGUID}';
type
ISomeInterface = interface(IDispatch)
['{SomeGUID}']
function SomeMethod(const AInput: WideString): WideString; safecall;
end;
TWrappingClass = class(TObject)
strict private
FInstance: ISomeInterface;
procedure CreateInstance;
public
procedure DoYourActualJob;
end;
procedure TWrappingClass.CreateInstance;
begin
FInstance := CreateComObject(CLASS_SomeClass) as ISomeInterface;
dbg(FInstance._AddRef); // Debugs 3
dbg(FInstance._AddRef); // Debugs 4
dbg(FInstance.Release); // Debugs 3
dbg(FInstance._AddRef); // Debugs 4
FInstance.SomeMethod(''); //Runs as expected
end;
procedure TWrappingClass.DoYourActualJob;
begin
CreateInstance;
dbg(FInstance._AddRef); //Debugs -1!
FInstance.SomeMethod(''); //AV
end;
В соответствии с примером экземпляр становится недействительным после того, как он уходит CreateInstance
метод. Компонент предназначен для работы со многими последовательными вызовами SomeMethod
и он работает, когда вызывается внутри одного метода. Может ли кто-нибудь дать мне понять, что на самом деле там происходит, почему мой экземпляр становится недействительным? Это проблема с моим кодом, с Delphi или с кодом компонента? Когда я меняю реализацию TWrappingClass
другому поставщику (то есть я меняю оба ISomeInterface
а также CLASS_SomeClass
) тогда все отлично работает.
РЕДАКТИРОВАТЬ: Поведение не меняется, когда я даже не звоню SomeMethod
, То есть после того как я уйду CreateInstance
, позвонить _AddRef
возвращает -1. Компонент, который я тестирую, находится здесь CadEditorX Вероятно, мне не разрешено подключать OCX без нарушения его лицензии.
1 ответ
Вы четко указали в вопросе, что ошибочное поведение происходит только с одним конкретным COM-объектом. Учитывая этот факт и то, что подсчет ссылок в Delphi COM, как известно, работает правильно, единственный разумный вывод заключается в том, что ошибка заключается в этом конкретном COM-объекте.
Единственный выход - связаться с продавцом этого COM-объекта и отправить ему отчет об ошибке.
Одна вещь, на которую стоит обратить внимание, с точки зрения возможного обхода, - это то, как вы создаете объект. Ты используешь CreateComObject
, Это получает идентификатор класса и возвращает IUnknown
, Это вызывает CoCreateInstance
передача идентификатора класса и запрос IUnknown
интерфейс. Затем вам нужно запросить ваш интерфейс, ISomeInterface
, Итак, ваш код выглядит так:
var
iunk: IUnknown;
intf: ISomeInteface;
....
CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER,
IUnknown, iunk);
iunk.QueryInterface(ISomeInterface, intf);
Тот факт, что у вас есть две интерфейсные переменные, одна IUnknown
и один ISomeInterface
объясняет, почему вы видите количество ссылок, которые вы делаете. Теперь вы можете подумать, что у вас есть только одна переменная интерфейса, но это не так. Их два, только один из них является неявным локальным. Вы можете увидеть это, посмотрев скомпилированный код и пройдя через отладчик.
Этот код:
procedure TWrappingClass.CreateInstance;
begin
FInstance := CreateComObject(CLASS_SomeClass) as ISomeInterface;
end;
компилируется так, как если бы это было (игнорируя проверку ошибок):
procedure TWrappingClass.CreateInstance;
var
iunk: IUnknown;
begin
iunk := CreateComObject(CLASS_SomeClass);
try
FInstance := CreateComObject(CLASS_SomeClass) as ISomeInterface;
finally
iunk := nil;
end;
end;
Возможно, COM-компонент не может обработать вызов Release
сделано на его IUnknown
интерфейс.
Таким образом, вы можете попытаться обойти это, используя CoCreateInstance
вместо CreateComObject
, Проходить ISomeInterface
как riid
параметр.
OleCheck(CoCreateInstance(CLASS_SomeClass, nil, CLSCTX_INPROC_SERVER
or CLSCTX_LOCAL_SERVER, ISomeInterface, FInstance));