Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2025-06

Cloning properties randomly generates duplicate name exception

6 messages · 3 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Luca Olivetti

Hello,

I'm using this code to copy the properties of a control:

procedure CloneProperties(const Source: TControl; const Dest: TControl);
var
   ms: TMemoryStream;
   OldName: string;
begin
   OldName := Source.Name;
   Source.Name := ''; // needed to avoid Name collision
   try
     ms := TMemoryStream.Create;
     try
       ms.WriteComponent(Source);
       ms.Position := 0;
       ms.ReadComponent(Dest);
     finally
       ms.Free;
     end;
   finally
     Source.Name := OldName;
   end;
end;

And, while it works 99% of the time, sometimes it raises a "duplicate name" exception.

I stepped into the code and the exception is generated here

Procedure TComponent.ValidateRename(AComponent: TComponent;
   const CurName, NewName: string);
begin
//!! This contradicts the Delphi manual.
   If (AComponentNil) and (CompareText(CurName,NewName)0) and
(AComponent.Owner = Self) and
      (FindComponent(NewName)Nil) then
       raise EComponentError.Createfmt(SDuplicateName,[newname]);
   If (csDesigning in FComponentState) and (FOwnerNil) then
     FOwner.ValidateRename(AComponent,Curname,Newname);
end;

but I cannot see why since an unique name has been generated in TReader.ReadRootComponent

           { We need an unique name }
           i := 0;
           { Don't use Result.Name directly, as this would influence
             FindGlobalComponent in successive loop runs }
           ResultName := CompName;
           Lock;
           try
             while Assigned(FindGlobalComponent(ResultName)) do
             begin
               Inc(i);
               ResultName := CompName + '_' + IntToStr(i);
             end;
             Result.Name := ResultName;
           finally
             Unlock;
           end;

This is with lazarus 3.8 under win32.

Bye

Luca Olivetti Tel. +34 935883004 Ext. 3010 https://wetron.es https://wecobots.com

Reply by email

Michael Van Canneyt
to Luca Olivetti
Luca Olivetti wrote 67 lines
Hello, I'm using this code to copy the properties of a control: procedure CloneProperties(const Source: TControl; const Dest: TControl); var ms: TMemoryStream; OldName: string; begin OldName := Source.Name; Source.Name := ''; // needed to avoid Name collision try ms := TMemoryStream.Create; try ms.WriteComponent(Source); ms.Position := 0; ms.ReadComponent(Dest); finally ms.Free; end; finally Source.Name := OldName; end; end; And, while it works 99% of the time, sometimes it raises a "duplicate name" exception. I stepped into the code and the exception is generated here Procedure TComponent.ValidateRename(AComponent: TComponent; const CurName, NewName: string); begin //!! This contradicts the Delphi manual. If (AComponentNil) and (CompareText(CurName,NewName)0) and (AComponent.Owner = Self) and (FindComponent(NewName)Nil) then raise EComponentError.Createfmt(SDuplicateName,[newname]); If (csDesigning in FComponentState) and (FOwnerNil) then FOwner.ValidateRename(AComponent,Curname,Newname); end; but I cannot see why since an unique name has been generated in TReader.ReadRootComponent { We need an unique name } i := 0; { Don't use Result.Name directly, as this would influence FindGlobalComponent in successive loop runs } ResultName := CompName; Lock; try while Assigned(FindGlobalComponent(ResultName)) do begin Inc(i); ResultName := CompName + '_' + IntToStr(i); end; Result.Name := ResultName; finally Unlock; end; This is with lazarus 3.8 under win32.

This check is only done for the top-level components. If the stream from Source contains a component A in it's component tree, which is already present in the tree below Dest, it will still collide.

Michael.

Reply by email

Luca Olivetti
to Michael Van Canneyt

El 17/6/25 a les 15:09, Michael Van Canneyt ha escrit:

This check is only done for the top-level components. If the stream from Source contains a component A in it's component tree, which is already present in the tree below Dest, it will still collide.

All the components are created as owned by the form (in its FormCreate event), or is this check based on the parent and not on the owner? Besides, I still don't understand why it works 99% of the time I create the form and fails randomly the remaining 1% of the time (the creation order and the components created are always the same). Note that it never fails when the form is autocreated, only when I create a new instance (and even then only from time to time).

Bye

Luca Olivetti Tel. +34 935883004 Ext. 3010 https://wetron.es https://wecobots.com

Reply by email

Mattias Gaertner
to Luca Olivetti: I'm using this code to copy the properties of a control: procedure CloneProperties(const…
Luca Olivetti wrote 28 lines
Hello, I'm using this code to copy the properties of a control: procedure CloneProperties(const Source: TControl; const Dest: TControl); var ? ms: TMemoryStream; ? OldName: string; begin ? OldName := Source.Name; ? Source.Name := ''; // needed to avoid Name collision ? try ??? ms := TMemoryStream.Create; ??? try ????? ms.WriteComponent(Source); ????? ms.Position := 0; ????? ms.ReadComponent(Dest); ??? finally ????? ms.Free; ??? end; ? finally ??? Source.Name := OldName; ? end; end; And, while it works 99% of the time, sometimes it raises a "duplicate name" exception.

For Dest itself or one of its children?

Show quoted text 27 lines
I stepped into the code and the exception is generated here Procedure TComponent.ValidateRename(AComponent: TComponent; ? const CurName, NewName: string); begin //!! This contradicts the Delphi manual. ? If (AComponentNil) and (CompareText(CurName,NewName)0) and (AComponent.Owner = Self) and ???? (FindComponent(NewName)Nil) then ????? raise EComponentError.Createfmt(SDuplicateName,[newname]); ? If (csDesigning in FComponentState) and (FOwnerNil) then ??? FOwner.ValidateRename(AComponent,Curname,Newname); end; but I cannot see why since an unique name has been generated in TReader.ReadRootComponent ????????? { We need an unique name } ????????? i := 0; ????????? { Don't use Result.Name directly, as this would influence ??????????? FindGlobalComponent in successive loop runs } ????????? ResultName := CompName; ????????? Lock; ????????? try ??????????? while Assigned(FindGlobalComponent(ResultName)) do

FindGlobalComponent needs to register RegisterFindGlobalComponentProc.

Show quoted text 11 lines
??????????? begin ????????????? Inc(i); ????????????? ResultName := CompName + '_' + IntToStr(i); ??????????? end; ??????????? Result.Name := ResultName; ????????? finally ??????????? Unlock; ????????? end; This is with lazarus 3.8 under win32.

Mattias

Reply by email

Luca Olivetti
to Luca Olivetti: El 17/6/25 a les 15:09, Michael Van Canneyt ha escrit: All the components are created as…

El 17/6/25 a les 15:24, Luca Olivetti via lazarus ha escrit:

Show quoted text 14 lines
El 17/6/25 a les 15:09, Michael Van Canneyt ha escrit: > This check is only done for the top-level components. If the stream from > Source contains a component A in it's component tree, which is already > present in the tree below Dest, it will still collide. All the components are created as owned by the form (in its FormCreate event), or is this check based on the parent and not on the owner? Besides, I still don't understand why it works 99% of the time I create the form and fails randomly the remaining 1% of the time (the creation order and the components created are always the same). Note that it never fails when the form is autocreated, only when I create a new instance (and even then only from time to time).

I'll add that the purpose is to clone a groupbox with 10 edits and 10 labels, I put them on the form at design time then generate several copies at run time.

I started with the following code to duplicate the groupboxes, but since I got random "duplicate control" error, I resorted to manually creating everything and just cloning the properties, which appeared to solve the problem but it actually didn't.

function DuplicateControl(const Source: TWinControl; const
owner,parent:TWinControl; const sufijo:String):TWinControl;
var
   I: Integer;
   CurrentControl, ClonedControl:TControl;
   procedure CloneProperties(const Source: TControl; const Dest: TControl);
   var
     ms: TMemoryStream;
     OldName: string;
   begin
     OldName := Source.Name;
     Source.Name := ''; // needed to avoid Name collision
     try
       ms := TMemoryStream.Create;
       try
         ms.WriteComponent(Source);
         ms.Position := 0;
         ms.ReadComponent(Dest);
       finally
         ms.Free;
       end;
     finally
       Source.Name := OldName;
     end;
   end;
   procedure CloneEvents(Source, Dest: TControl);
   var
     I: Integer;
     PropList: TPropList;
   begin
     for I := 0 to GetPropList(Source.ClassInfo, [tkMethod], @PropList)
- 1 do
       SetMethodProp(Dest, PropList[I], GetMethodProp(Source, PropList[I]));
   end;
begin
   //duplicates the control..
   result:=TWinControlClass(Source.ClassType).Create(Owner);
   CloneProperties(Source, Result);
   //..and all the contained controls
   for I := Source.ControlCount - 1 downto 0 do
   begin
     CurrentControl := Source.Controls[I];
     //same owner (the form) to find the SessionProperties
     ClonedControl := TControlClass(CurrentControl.ClassType).Create(owner);
     ClonedControl.Parent := Result;
     CloneProperties(CurrentControl, ClonedControl);
     //for some reason the font isn't cloned properly, I didn't want to
investigate anymore
     ClonedControl.Font.Style:=[];
     ClonedControl.Name := CurrentControl.Name+sufijo;
     CloneEvents(CurrentControl, ClonedControl);
   end;
   Result.Parent:=Parent;
   CloneEvents(Source, Result);
end;

Bye

Luca Olivetti Tel. +34 935883004 Ext. 3010 https://wetron.es https://wecobots.com

Reply by email

Luca Olivetti
to Mattias Gaertner: For Dest itself or one of its children? FindGlobalComponent needs to register…

El 17/6/25 a les 15:32, Mattias Gaertner via lazarus ha escrit:

Show quoted text 5 lines
> > And, while it works 99% of the time, sometimes it raises a "duplicate > name" exception. For Dest itself or one of its children?

Mmmh, maybe.
As I said in another message, I create a groupbox then clone the properties of the original groupbox, which contains 10 labels and 10 edits (mind me, the owner is still the form, the groupbox is only the parent).
But then it should reliably fail everytime, shouldn't it?

Bye

Luca Olivetti Tel. +34 935883004 Ext. 3010 https://wetron.es https://wecobots.com

Reply by email