🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: borland.public.delphi.com.activex.using
2 messages
2 total messages Started by David Petrie Thu, 07 Aug 2008 02:48
Calling COM object contained inside DLL - How to unload DLL when finished calling?
#186
Author: David Petrie
Date: Thu, 07 Aug 2008 02:48
260 lines
8262 bytes
Hi Everyone,

I'm not sure if this is the correct group to post this.  If not, my 
apologies.

I have a problem with a COM object that I am calling.  I am not sure if it 
is a problem with my code, or a problem with my understanding :-)  If anyone 
here is able to shed some light on this, it would be most appreciated.  I 
will briefly outline my issue, then provide some more detailed info for 
those who are interested.


ISSUE IN BRIEF

I am calling a third part COM object that I have no control over, contained 
inside a DLL.  I am able to load and call the COM object fine.  However, my 
observation (using the Delphi 2007 Event Viewer when debugging) is that once 
the DLL is loaded by my Delphi application when creating the COM object, the 
DLL does not get unloaded until the Delphi application terminates - 
regardless of when I explicitly free the COM object.  My problem with this 
is that it seems the DLL is somehow caching information.  Being a 3rd party 
DLL it is difficult to test this hypothesis.  Instead, what I am simply 
trying to do is ensure that the DLL containing the COM object is loaded 
fresh each time I need to call it, and then unloaded when I am finished.

Is it possible to ensure that the DLL will be unloaded once I have finished 
using it, and then loaded afresh if I need it again?


MORE DETAIL

In reality, the DLL is a .NET assembly, exposed to Win32 as a COM object. 
The DLL is registered using "regasm".  I am calling and loading this COM 
object from a Delphi 2007 Win32 application.  To test the COM behaviour in 
Delphi 2007 I created a dummy application (a "spike").  In this app I have 
observed the same behaviour as described above.  So I don't think the fact 
it is a .NET DLL has anything to do with it.

In my dummy project group, I created a really simple Win32 ActiveX dll 
containing a COM server.  (I am happy to email the full source code to 
anyone, if desired.)  The DLL is called "ComTest.dll".  The COM object is 
"ComTest.Test".  The first code section at the end of this post is the 
"TestComObj.pas" unit which contains TTest - the implementation of the COM 
object.  Basically there is a "ping" method which just shows a message 
dialog, and I have also overridden the destructor to display another message 
so I can see when it gets destroyed.  The DLL is registered using 
"RegSvr32".

The other app in my project group is "ComLauncher.exe", a standard Win32 VCL 
Forms app.  This has a main form with three buttons on it.  The "Main.pas" 
unit is the second code extract at the end of this post.  The three buttons 
do this:

* BtnDLL - This simply loads and unloads the DLL without calling the COM 
object
* BtnNormal - This calls the COM object in the normal way
* BtnExplicit - This has calls that explicitly load and unload the DLL, as 
well as call the COM object

As you will notice, I have ShowMessage calls at each step.  Following is the 
sequence of messages I receive.  I have also noted when I observe the DLL to 
be loaded and unloaded in the Delphi event viewer.  Each button is clicked 
after restarting the application.

--> BtnDLL

  'At Start'
    * DLL loads here *
  'After Load DLL'
    * DLL unloads here *
  'After free DLL - At End'

--> BtnNormal

  'At Start'
  'Before launch COM'
    * DLL loads here *
  'After launch'
  'Hello World'
  'After Ping'
  'After free COM Object'
  'Destroying TTest'
  'At End'

If you click this button again, the same happens, except that the DLL does 
not load, because it is already loaded.

--> BtnExplicit

  'At Start'
    * DLL loads here *
  'After Load DLL'
  'Before launch COM'
  'After launch'
  'Hello World'
  'After Ping'
  'After free COM Object'
  'Destroying TTest'
  'After free DLL - At End'


The BtnDLL case happens as I expect.

The BtnNormal case keeps the DLL loaded once finished.  Is this normal 
behaviour?  The COM object itself is freed, as indicated by the message I 
put in the destructor.  Interestingly, as an aside, it is not freed when I 
expect it to be.  The Delphi Help indicates that it should be freed when you 
assign "Unassigned" to it (App := Unassigned).  However, it does not seem to 
be freed until the local variable "App" goes out of scope.  This is not 
really an issue, just a curiosity.

Thinking that keeping the DLL loaded may be a "feature" to improve 
performance for subsequent calls, I decided to put in some calls to 
explicitly load and unload the DLL.  This is done in "BtnExplicit". 
However, despite my explicit calls, the DLL stays loaded until the 
application terminates.

Does anyone know how I can get the DLL to unload when I am done with it?

Regards,

David.

_____________________________________

CODE EXTRACTS


--> TestComObj.pas

unit TestComObj;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  ComObj, ActiveX, ComTest_TLB, StdVcl;

type
  TTest = class(TAutoObject, ITest)
  protected
    procedure Ping; safecall;
  public
    destructor Destroy; override;
  end;

implementation

uses ComServ, Dialogs;

destructor TTest.Destroy;
begin
  ShowMessage('Destroying TTest');
  inherited Destroy;
end;

procedure TTest.Ping;
begin
  ShowMessage('Hello World');
end;

initialization
  TAutoObjectFactory.Create(ComServer, TTest, Class_Test,
    ciMultiInstance, tmApartment);
end.


--> Main.pas

unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TFrmMain = class(TForm)
    BtnDLL: TButton;
    BtnNormal: TButton;
    BtnExplicit: TButton;
    procedure BtnNormalClick(Sender: TObject);
    procedure BtnExplicitClick(Sender: TObject);
    procedure BtnDLLClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FrmMain: TFrmMain;

implementation

uses ComObj;

{$R *.dfm}

procedure TFrmMain.BtnDLLClick(Sender: TObject);
var
  DLLHandle: THandle;
begin
  ShowMessage('At Start');
  DLLHandle := LoadLibrary('ComTest.dll');
  ShowMessage('After Load DLL');
  FreeLibrary(DLLHandle);
  ShowMessage('After free DLL - At End');
end;

procedure TFrmMain.BtnNormalClick(Sender: TObject);

  procedure Sub;
  var
    App: Variant;
  begin
    ShowMessage('Before launch COM');
    App := CreateOleObject('ComTest.Test');
    ShowMessage('After launch');
    App.Ping;
    ShowMessage('After Ping');
    App := Unassigned;
    ShowMessage('After free COM Object');
  end;

begin
  ShowMessage('At Start');
  Sub;
  ShowMessage('At End');
end;

procedure TFrmMain.BtnExplicitClick(Sender: TObject);

  procedure Sub;
  var
    App: Variant;
  begin
    ShowMessage('Before launch COM');
    App := CreateOleObject('ComTest.Test');
    ShowMessage('After launch');
    App.Ping;
    ShowMessage('After Ping');
    App := Unassigned;
    ShowMessage('After free COM Object');
  end;

var
  DLLHandle: THandle;
begin
  ShowMessage('At Start');
  DLLHandle := LoadLibrary('ComTest.dll');
  ShowMessage('After Load DLL');
  Sub;
  FreeLibrary(DLLHandle);
  ShowMessage('After free DLL - At End');
end;

end.
Re: Calling COM object contained inside DLL - How to unload DLL when finished calling?
#188
Author: Remy Lebeau (TeamB)
Date: Thu, 07 Aug 2008 16:38
29 lines
2070 bytes
"David Petrie" <n/a> wrote in message 
news:489a62d8@newsgroups.borland.com...

> However, my observation (using the Delphi 2007 Event Viewer
> when debugging) is that once the DLL is loaded by my Delphi
> application when creating the COM object, the DLL does not get
> unloaded until the Delphi application terminates - regardless of
> when I explicitly free the COM object.

It is not supposed to release the DLL right away.  COM keeps the DLL loaded 
in case you want to use it again in the same process.

> Is it possible to ensure that the DLL will be unloaded once I have
> finished using it, and then loaded afresh if I need it again?

To force COM to release idle DLLs, call CoFreeUnusedLibraries().


> Interestingly, as an aside, it is not freed when I expect it to be.
> The Delphi Help indicates that it should be freed when you assign
> "Unassigned" to it (App := Unassigned).

Assign nil to it, not Unassigned:

    App := nil;


Gambit 
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads