RemObjects memory leaks in Delphi

I have the following code which creates an instance of an RemObjects service and makes a call to a .net server

class function TLabelPrintingServiceProxy.GetInstance: ILabelPrintingManager;
var
  LRoRemoteService: TRoRemoteService;
begin
  LRoRemoteService := TRoRemoteService.Create(nil);
  LRoRemoteService.Message := TROSOAPMessage.Create();
  LRoRemoteService.Channel := TROIndyHTTPChannel.Create(nil);
  LRoRemoteService.Channel.TargetUri := TROUri.Create(ILabelPrintingIntf.LabelPrintingManager_EndPointURI);

  Result := (LRoRemoteService as ILabelPrintingManager);
end;

call to the .net service is performed like this:

  try
        Result := BinaryArray.Create;
        LLabelPrintingManager := TLabelPrintingServiceProxy.GetInstance();
        Result.Add(LLabelPrintingManager.GetVSSLabelImage(APrintJob));
    finally 
        TLabelPrintingServiceProxy.ReleaseLabelPrintingServiceProxyInstance(LLabelPrintingManager);
    end;

After the call is made the LLabelPrintingManager interface should be released automatically by RemObjects, but it isn’t and leaks the objects used.

I’ve tried on the ReleaseLabelPrintingServiceProxyInstance (code bellow) to release manually all the objects from the service instance, but it’s still leaking

class procedure TLabelPrintingServiceProxy.ReleaseLabelPrintingServiceProxyInstance(aILabelPrintingManagerIntf: ILabelPrintingManager);
var
  lProxy: TRoProxy;
begin
  lProxy := TROProxy(aILabelPrintingManagerIntf);
  TROIndyHTTPChannel(lProxy.__TransportChannel).TargetUri.Free;
//  TROIndyHTTPChannel(lProxy.__TransportChannel).Free; this is generating an AV
  TRoMessage(lProxy.__Message).free;

  TRoRemoteService(aILabelPrintingManagerIntf).Free; end;

I’ve tried to create the call using CoLabelPrintingManager.Create(const aMessage: IROMessage; aTransportChannel: IROTransportChannel) and passing message and channel as interfaced objects but it also leaks. Setting ILabelPrintingManager to nil has also no effect

I’m missing something?

TRoRemoteService is descendant of TComponent so you need to manually destroy it if was created with nil as AOwner.

Why you can’t create interface just passing URL like

LLabelPrintingManager  := CoLabelPrintingManager.Create(ILabelPrintingIntf.LabelPrintingManager_EndPointURI);  

?
by default, each Co*class has 3 methods like

  CoNewService = class(System.TObject)
  public
    class function Create(const aMessage: IROMessage; aTransportChannel: IROTransportChannel): INewService; overload;
    class function Create(const aUri: TROUri; aDefaultNamespaces: String = ''): INewService; overload;
    class function Create(const aUrl: String; aDefaultNamespaces: String = ''): INewService; overload;
  end;

so you can use any of these methods for creating interface.
Note: TRORemoteService does the same so you can create interface w/o this component.

When I create the service as indicated

Result := CoLabelPrintingManager.Create(ILabelPrintingIntf.LabelPrintingManager_EndPointURI);

and make the call to .net service I get the following error:
Untitled2

I want to free the TRoRemoteService instance, but when I’m trying to cast ILabelPrintingManager interface to TRoRemoteService the result is all the time nil.

How can I cast back the proxy interface to TRoRemoteService, so I can release all it’s members?

seems, it creates BinMessage instead of SOAPMessage
use another overload, where you can pass message type:

class function Create(const aMessage: IROMessage; aTransportChannel: IROTransportChannel): INewService; overload;

As suggested

class function TLabelPrintingServiceProxy.GetInstance: ILabelPrintingManager;
var
    lIRoMessage: IRoMessage;
    lIRoChannel: IROTransportChannel;
begin
  lIRoMessage := TROSOAPMessage.Create();
  lIRoChannel := TROIndyHTTPChannel.Create(nil);
  TROIndyHTTPChannel(lIRoChannel).TargetUrl := ILabelPrintingIntf.LabelPrintingManager_EndPointURI;
  Result := CoLabelPrintingManager.Create(lIRoMessage,lIRoChannel);
end;

and the leak report

Is there a way to cast ILabelPrintingManager back to TRoRemoteSevice? How should I get rid of these memory leaks?

try to use this code. here you return TRoRemoteService in GetInstance so no memory leaks should be now

class function TLabelPrintingServiceProxy.GetInstance: TRoRemoteService;
var
  LRoRemoteService: TRoRemoteService;
begin
  LRoRemoteService := TRoRemoteService.Create(nil);
  LRoRemoteService.Message := TROSOAPMessage.Create();
  LRoRemoteService.Channel := TROIndyHTTPChannel.Create(nil);
  LRoRemoteService.Channel.TargetURL := ILabelPrintingIntf.LabelPrintingManager_EndPointURI;
  Result := LRoRemoteService;
end;
  try
        Result := BinaryArray.Create;
        LLabelPrintingManager := TLabelPrintingServiceProxy.GetInstance();
        Result.Add((LLabelPrintingManager as ILabelPrintingManager).GetVSSLabelImage(APrintJob));
    finally 
        TLabelPrintingServiceProxy.ReleaseLabelPrintingServiceProxyInstance(LLabelPrintingManager);
    end;
class procedure TLabelPrintingServiceProxy.ReleaseLabelPrintingServiceProxyInstance(aILabelPrintingManagerIntf: TRoRemoteService);
begin
  aILabelPrintingManagerIntf.Channel.Free;
  aILabelPrintingManagerIntf.Message.free;
  aILabelPrintingManagerIntf.Free; 
end;

Thanks Evgeny. Seems to work and leaks only a TROUri instance.

if you use something like

LRoRemoteService.Channel.TargetUri := TROUri.Create(ILabelPrintingIntf.LabelPrintingManager_EndPointURI);

this is a reason for such memory leak because TROTransportChannel doesn’t store TROUri - it just assigns properties of aUri to internal object:

procedure TROTransportChannel.SetTargetUri(const aUri: TROUri);
begin
  if fTargetUri <> aUri then TargetUri.Assign(aUri);
end;

TargetUri is destroying in

destructor TROTransportChannel.Destroy;
begin
...
  fTargetUri.Free;
...
end;

You shouldn’t destroy (i.e. TargetUri) internal object because it is destroyed in destructor.
FastMM should display callstack where is leaked TROUri was created.

use TargetURL instead of TargetUri because it does the same as you want:

procedure TROTransportChannel.SetTargetUrl(const aValue: string);
var
  lUri: TROUri;
begin
  lUri := TROUri.Create(aValue);
  try
    SetTargetUri(lUri);
  finally
    lUri.Free;
  end;
end;

That was the reason for deleting my own post, I’ve solved it using TargetURL

Thanks!