Измените сигнатуру вашего метода, чтобы она возвращала интерфейс, а не экземпляр класса SomeObject
.Вы можете сделать это в редакторе библиотеки типов следующим образом:
Сгенерированный код будет:
function THelper.CreateSomeObject: ISomeObject;
begin
end;
Edit 1
Согласно комментарию: Несмотря на то, что вы предоставили много текста в своем вопросе, ему все еще не хватает важной информации.Вы упомянули некоторые операции с указателями, но это не то, с чем вам следует иметь дело в типичном сценарии при разработке COM-сервера.
Поэтому я попытался воссоздать ваш сценарий самостоятельно в Delphi 7.(самая старая версия Delphi, которую я установил).Я создал COM-сервер (проект ActiveX Library
в Delphi), аналогичный приведенному выше.Моя реализация метода CreateSomeObject
была:
function THelper.CreateSomeObject: ISomeObject;
begin
Result := TSomeObject.Create;
end;
Реализация метода TSomeObject.HelloWorld
не важна.Затем я зарегистрировал сервер через функцию IDE Run > Register ActiveX Server
.После этого я создал пример консольного приложения в Delphi, импортировал библиотеку типов (Project > Import Type Library
) и добавил несколько строк кода в основную программу:
uses
ActiveX, COMTest_TLB;
var
_Helper: IHelper;
_SomeObject: ISomeObject;
begin
CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
_Helper := CoHelper.Create;
_SomeObject := _Helper.CreateSomeObject;
_SomeObject.HelloWorld;
end.
Консольное приложение завершилось без сбоев или неожиданного результата.Все идет нормально.Затем я создал образец консольного приложения C # .NET (.NET 4.5.2) со ссылкой на мою библиотеку COMTest:
using System;
using COMTest;
class Program
{
[STAThread]
static void Main(string[] args)
{
var helper = new Helper();
var someObject = helper.CreateSomeObject();
someObject.HelloWorld();
}
}
И действительно, приложение зависало с AccessViolationException
.Я быстро настроил отладку COM-сервера, настроив хост-приложение на консольное приложение .NET и включив символы удаленной отладки в настройках компоновщика проекта (я уверен, что вы уже поняли это).Создание экземпляра TSomeObject
прошло гладко, но присвоение Result
не удалось.
Существует некоторая магия компилятора при назначении значения переменной управляемого типа (в данном случае - интерфейс).Он начинается с очистки пункта назначения, который в основном является вызовом _Release
, если пункт назначения не является nil
.И, к моему удивлению, в случае клиента консольного приложения .NET это не так!Поэтому я изменил реализацию так:
function THelper.CreateSomeObject: ISomeObject;
begin
Pointer(Result) := nil;
Result := TSomeObject.Create;
end;
Очистка результата перед его первым использованием в качестве интерфейса.Я не стал вдаваться в подробности того, почему это происходит, но я обязательно сделаю.Я также проверю это с более новой версией Delphi и опубликую свои выводы в другой редакции этого ответа.
Здесь вы можете найти некоторые замечательные ресурсы, связанные с разработкой COM в Delphi.http://www.techvanguards.com/com/
Отказ от ответственности: я никоим образом не связан с этим сайтом, я просто нахожу его невероятно полезным.
Редактировать 2
Методы интерфейса COM должны возвращать HRESULT
по соглашению.Это механизм по умолчанию в COM для сообщения об ошибках.Возврат дополнительных значений из метода должен осуществляться через параметры с модификатором [Out]
.В качестве альтернативы параметр может быть помечен модификатором [Out, RetVal] (обычно последним), чтобы указать возвращаемое значение метода.Обратите внимание, что параметры [Out] передаются по ссылке, которую необходимо указать с помощью дополнительного символа звездочки (*), добавляемого к имени типа в редакторе библиотеки типов.Таким образом, ISomeObject*
становится [Out] ISomeObject**
.
. Delphi поддерживает safecall
соглашение о вызовах и, таким образом, может исключить HRESULT
возвращаемое значение из сигнатуры вашего метода, обернув любое необработанное исключение в вашем методе и передав егообратно в регистр EAX , позволяя параметру [Out, RetVal] стать возвращаемым значением.Но это поддерживается только для двойных интерфейсов (см. Вкладку Flags интерфейса), которые получены из IDispatch
.Чтобы избежать реализации IDispatch
методов, вы можете преобразовать свой легкий COM-объект в объект автоматизации (TAutoObject
), если вы этого еще не сделали.Этого можно достичь, выбрав опцию «Объект автоматизации» вместо «COM-объект» при добавлении нового элемента в вашу библиотеку ActiveX.
Так выглядит определение метода при преобразовании в safecall
:
А вот сгенерированный код с тривиальной реализацией:
type
THelper = class(TAutoObject, IHelper)
protected
function CreateSomeObject: ISomeObject; safecall;
end;
function THelper.CreateSomeObject: ISomeObject;
begin
Result := TSomeObject.Create;
end;