Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Calling COM object contained inside DLL - How to unload DLL when finished calling?

172 views
Skip to first unread message

David Petrie

unread,
Aug 6, 2008, 10:48:00 PM8/6/08
to
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.

Remy Lebeau (TeamB)

unread,
Aug 7, 2008, 12:38:18 PM8/7/08
to

"David Petrie" <n/a> wrote in message
news:489a...@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


0 new messages