I have a TMyFileStream that extend TFileStream.
type
TMyFileStream = class(TFileStream)
In TMyFileStream.Destroy i have some procedure used for logging server downloads. The file stream is passed in the response stream, when the client close connection with server or terminate download of the response the object is destroyed and the destructor log information like bytes send/file size.
This procedure that could raise exception first of call parent decostructor. I want protect the parent destructor from child exception.
basic example
destructor TMyFileStream.Destroy;
begin
MyExceptionMethod();
inherited;
end;
First solution i found is call the inherited destructor first of all
destructor TMyFileStream.Destroy;
begin
inherited;
MyExceptionMethod();
end;
another solution is catch and ignore exception
destructor TMyFileStream.Destroy;
begin
try
MyExceptionMethod();
except
// nothing
end;
inherited;
end;
another solution is catch and call the inherited destructor in finally
destructor TMyFileStream.Destroy;
begin
try
try
MyExceptionMethod();
except
// nothing
end;
finally
inherited;
end;
end;
what is the right way?
An alternative would be to override BeforeDestruction, there inherited is not related to the actual de-allocation of the instance's memory.
Related
Is this code valid:
TMyObj = class(Tobject)
public
FThread: TThread;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
constructor TMyObj.Create(AOwner: TComponent);
begin
inherited;
FThread:= TThread.createAnonymousThread(
procedure
begin
while not FThread.CheckTerminated do sleep(10000);
end);
FThread.start;
end;
destructor TMyObj.Destroy;
begin
inherited;
end;
my question is after i do
destructor TMyObj.Destroy;
begin
inherited;
end;
I mean after the object TMyObj is free, what will be the value of FThread inside
FThread:= TThread.createAnonymousThread(
procedure
begin
while not FThread.CheckTerminated do sleep(10000);
end);
No, this code is not valid.
It is important to know that FThread is not captured, but the reference to the containing instance. So the anonymous method code rather looks like this:
FThread := TThread.createAnonymousThread(
procedure
begin
while not Self.FThread.CheckTerminated do sleep(10000);
end);
The compiler generates an object that implements the anonymous method with a field called Self that captures the reference to the TMyObj instance. If that instance gets destroyed while the anonymous method is alive within the thread, it will have a dangling reference.
A thread created with TThread.CreateAnonymousThread() is destroyed automatically when it finishes. So if you store that in a field for later access, you at least need to turn FreeOnTerminate off and not reference it via a captured variable to the thread itself, but rather with TThread.Current. Within the anonymous method, this will give you the thread that method is running in.
I have TMyClass, a class derived from TObject. It has a TTimer. Every few minutes, from Timer.OnTimer I check a web page. When the web page changes, I am done and I want to free MyClass. How do I free it?
My question is similar to this one BUT my 'control' is not a TControl. It is descendent of TObject. So, Messages won't work.
Obviously, the solution will be to derive my class from TControl or higher. But let's say I don't want to do that. What would be the solution in this case?
The basic idea behind using a message is correct: ensure that the object gets freed at a later point, after whatever code is currently calling it is finished.
A few years ago, I wrote a Delayed Action unit that gives you a simple way to accomplish this same effect without a TControl. You just call DelayExec and pass an anonymous method to it that will free the object, and it sets up a message internally that makes it happen once the message queue gets pumped.
To receive messages you need to have window handle. You can allocate one using AllocateHWnd, something like
type
TMyClass = class(TObject)
private
FHandle: HWND;
procedure MyWndProc(var Msg: TMessage);
public
constructor Create; virtual;
destructor Destroy; override;
end;
constructor TMyClass.Create();
begin
inherited Create();
FHandle := AllocateHWnd(myWndProc);
end;
destructor TMyClass.Destroy;
begin
DeallocateHWnd(FHandle);
inherited;
end;
procedure TMyClass.MyWndProc(var Msg: TMessage);
begin
case Msg.Msg of
CM_RELEASE: begin
Free;
end;
else Msg.Result := DefWindowProc(FHandle, Msg.Msg, Msg.WParam, Msg.LParam);
end;
end;
Now you can post messages to the object using the FHandle as demonstrated in the post youre reffering to.
Just before my TCustomWinControl is destroyed permanently, I need to do somthing with its handle.
If I try to access its handle in the destructor I get an error:
"Control "xxx" has no parent window".
So what is the last stage before TWinControl Destructor where its handle (HandleAllocated) is still valid?
type
TPanel = class(ExtCtrls.TPanel)
protected
procedure DestroyWindowHandle; override;
public
procedure BeforeDestruction; override;
end;
procedure TPanel.DestroyWindowHandle;
begin
Beep;
if csDestroying in ComponentState then Beep;
inherited;
end;
procedure TPanel.BeforeDestruction;
begin
if HandleAllocated then Beep;
inherited;
end;
There is no Beep.
Update
It is more complex than I originally thought. Your control lives on a form, and the control's death is provoked by the death of that form. When a form is destroyed, the child windows are also destroyed. So, the Win32 API is responsible for destroying your window. The VCL keeps track of this by responding to the WM_NCDESTROY message:
procedure TWinControl.WMNCDestroy(var Message: TWMNCDestroy);
begin
inherited;
FHandle := 0;
FShowing := False;
end;
So, I guess you could handle WM_NCDESTROY yourself. Look for csRecreating in ControlState to switch behaviour based on whether or not the window destruction is related to VCL window re-creation.
An interesting point to note here is that there's no reason why the destructor of your control has to be called. If it is not owned by the form then your control won't be destroyed. You could then re-parent it onto another form. So WM_NCDESTROY really is the right hook.
Original answer
The source code of the destructor looks like this:
destructor TWinControl.Destroy;
var
I: Integer;
Instance: TControl;
begin
Destroying;
if FDockSite then
begin
FDockSite := False;
RegisterDockSite(Self, False);
end;
FDockManager := nil;
FDockClients.Free;
if Parent <> nil then RemoveFocus(True);
if FHandle <> 0 then DestroyWindowHandle;
I := ControlCount;
while I <> 0 do
begin
Instance := Controls[I - 1];
Remove(Instance);
Instance.Destroy;
I := ControlCount;
end;
FBrush.Free;
{$IFDEF LINUX}
if FObjectInstance <> nil then WinUtils.FreeObjectInstance(FObjectInstance);
{$ENDIF}
{$IFDEF MSWINDOWS}
if FObjectInstance <> nil then Classes.FreeObjectInstance(FObjectInstance);
{$ENDIF}
inherited Destroy;
end;
The call to the Win32 API DestroyWindow is made in this line:
if FHandle <> 0 then DestroyWindowHandle;
So you need to run your code before then.
You could override DestroyWindowHandle and do your work there. That would work well so long as the event you need to deal with is the destruction of the window. But bear in mind that DestroyWindowHandle will be called when the window is re-created.
If you need to do something related to the destruction of the VCL control, then you would be best overriding BeforeDestruction. Or as an alternative, you could override DestroyWindowHandle and in there test for csDestroying in the ComponentState.
I found this source code from a Delphi sample codes, and
I am adding a control or component inside a Delphi dynamic DLL, I can't figure it out,
library DLLEntryLib;
uses
SysUtils,
Windows,
Dialogs,
Classes,
msHTML,
SHDocVw;
type
TMyWeb = class(TWebBrowser)
constructor create(Aowner: TComponent); override;
end;
var
web: TMyWeb;
// Initialize properties here
constructor TMyWeb.Create(AOwner: TComponent);
begin
inherited Create(Self);
end;
procedure getweb;
begin
web := TmyWeb.create(nil);
web.Navigate('http://mywebsite.com');
end;
procedure xDLLEntryPoint(dwReason: DWord);
begin
case dwReason of
DLL_PROCESS_ATTACH:
begin
getweb; //I THINK THE ERROR IS HERE, HOW TO WORK THIS OUT?
ShowMessage('Attaching to process');
end;
DLL_PROCESS_DETACH: ShowMessage('Detaching from process');
DLL_THREAD_ATTACH: MessageBeep(0);
DLL_THREAD_DETACH: MessageBeep(0);
end;
end;
begin
{ First, assign the procedure to the DLLProc variable }
DllProc := #xDLLEntryPoint;
{ Now invoke the procedure to reflect that the DLL is attaching to the
process }
xDLLEntryPoint(DLL_PROCESS_ATTACH);
end.
//IN MY APPLICATION FORM.
procedure TMainForm.btnLoadLibClick(Sender: TObject);
begin
if LibHandle = 0 then
begin
LibHandle := LoadLibrary('DLLENTRYLIB.DLL');
if LibHandle = 0 then
raise Exception.Create('Unable to Load DLL');
end
else
MessageDlg('Library already loaded', mtWarning, [mbok], 0);
end;
How do I get rid of the error?
raise to many consicutive exception
When you write:
inherited Create(Self);
you should write
inherited Create(AOwner);
You are asking the control to own itself. That just cannot work. That quite possibly leads to a non-terminated recursion if the constructor fails.
The other big problem is that you are creating a web browser control inside DllMain. That's a very big no-no. You'll want to stop doing that. Move that code into a separate exported function. Do nothing in DllMain.
Presumably the caller has already initialized COM. If not, you will need to ensure that the caller does so. If the caller is a VCL forms app then COM will be initialized automatically.
How would you "extract" nested try/finally blocks from a routine into a reusable entity? Say I have
procedure DoSomething;
var
Resource1: TSomeKindOfHandleOrReference1;
Resource2: TSomeKindOfHandleOrReference2;
Resource3: TSomeKindOfHandleOrReference3;
begin
AcquireResource1;
try
AcquireResource2;
try
AcquireResource3;
try
// Use the resources
finally
ReleaseResource3;
end;
finally
ReleaseResource2;
end;
finally
ReleaseResource1;
end;
end;
and want something like
TDoSomething = record // or class
strict private
Resource1: TSomeKindOfHandleOrReference1;
Resource2: TSomeKindOfHandleOrReference2;
Resource3: TSomeKindOfHandleOrReference3;
public
procedure Init; // or constructor
procedure Done; // or destructor
procedure UseResources;
end;
procedure DoSomething;
var
Context: TDoSomething;
begin
Context.Init;
try
Context.UseResources;
finally
Context.Done;
end;
end;
I want this to have the same exception-safety as the nested original. Is it enough to zero-initialize the ResourceN variables in TDoSomething.Init and do some if Assigned(ResourceN) then checks in TDoSomething.Done?
There are three things about classes that make this idiom safe and easy:
During the memory-allocation phase of the constructor (before the real constructor body runs), class-reference fields get initialized to nil.
When an exception occurs in a constructor, the destructor is called automatically.
It's always safe to call Free on a null reference, so you never need to check Assigned first.
Since the destructor can rely on all fields to have known values, it can safely call Free on everything, regardless of how far the constructor got before crashing. Each field will either hold a valid object reference or it will be nil, and either way, it's safe to free it.
constructor TDoSomething.Create;
begin
Resource1 := AcquireResource1;
Resource2 := AcquireResource2;
Resource3 := AcquireResource3;
end;
destructor TDoSomething.Destroy;
begin
Resource1.Free;
Resource2.Free;
Resource3.Free;
end;
Use it the same way you use any other class:
Context := TDoSomething.Create;
try
Context.UseResources;
finally
Context.Free;
end;
Yes, you can use a single try/finally/end block for multiple resources with zero-initialization.
Another possible solution can be found in Barry Kelly blog
The pattern with testing on Assigned in finally is used in the Delphi source. You do kind of the same thing but I think you should move Context.Init to capture exception from Context.Init.
procedure DoSomething;
var
Context: TDoSomething;
begin
try
Context.Init;
Context.UseResources;
finally
Context.Done;
end;
end;
Edit 1 This is how you should do it without Context.Init and Context.Done. If you place all AquireResource code before try you will not free Resource1 if you get an exception in AcquireResource2
procedure DoSomething;
var
Resource1: TSomeKindOfHandleOrReference1;
Resource2: TSomeKindOfHandleOrReference2;
Resource3: TSomeKindOfHandleOrReference3;
begin
Resource1 := nil;
Resource2 := nil;
Resource3 := nil;
try
AcquireResource1;
AcquireResource2;
AcquireResource3;
//Use the resources
finally
if assigned(Resource1) then ReleaseResource1;
if assigned(Resource2) then ReleaseResource2;
if assigned(Resource3) then ReleaseResource3;
end;
end;