views:

121

answers:

2

Is there a way to access (and call) procedures like _CopyArray that are defined in the interface in the unit System?

NB: I am trying to create a routine that makes a deep clone of any dynamic array, and do not use Delphi 2010 (using Delphi 2007).

The reason why I am trying to solve this without using Copy is the fact that I have only a pointer where the dynamic array is located (the pointer that is) plus a typeinfo reference. I cannot call the Copy function because it implicitly needs to fill in the typeinfo.

SOLUTION: You need to reference it by replacing the _ with an @ and scoping it with system.

procedure CopyArray( dest, source, typeInfo: Pointer; cnt: Integer );
asm
    PUSH    dword ptr [EBP+8]
    CALL    system.@CopyArray
end;

type
    PObject = ^TObject;

function TMessageRTTI.CloneDynArray( Source: Pointer; T: TTypeRecord ): Pointer;
var
    TypeInfo: TTypeRecord;
    L:        Integer;
    PObj:     PObject;
    PArr:     PPointer;
begin
    Assert( T.TypeKind = tkDynArray );

    // set size of array
    Result := nil;
    L := Length( TIntegerDynArray( Source ) );
    if L = 0 then Exit;
    DynArraySetLength( Result, T.TypeInfo, 1, @L );

    if Assigned( T.TypeData^.elType ) then TypeInfo := ByTypeInfo( T.TypeData^.elType^ ) else TypeInfo := nil;
    if Assigned( TypeInfo ) then begin
        case TypeInfo.TypeKind of
            tkClass: begin
                PObj := Result;
                while L > 0 do begin
                    PObj^ := CloneObject( PObject( Source )^ );
                    Inc( PObject( Source ) );
                    Inc( PObj );
                    Dec( L );
                end;
            end;
            tkDynArray: begin
                PArr := Result;
                while L > 0 do begin
                    PArr^ := CloneDynArray( PPointer( Source )^, TypeInfo );
                    Inc( PPointer( Source ) );
                    Inc( PArr );
                    Dec( L );
                end;
            end;
            else CopyArray( Result, Source, TypeInfo.TypeInfo, L );
        end;
    end else begin
        // We can simply clone the data
        Move( Source^, Result^, L * T.ElementSize );
    end;
end;
+3  A: 

The comment by Andreas Rejbrand is actually an answer - the _CopyArray procedure is called automaticaly when you copy complicated arrays. For example, set a breakpoint in _CopyArray and run the following code (should be compiled with debug .dcu to activate the breakpoint):

procedure TForm1.Button4Click(Sender: TObject);
type
  TArr2D = array of TBytes;

var
  A, B: TArr2D;

begin
  A:= TArr2D.Create(TBytes.Create(1, 2, 3), TBytes.Create(4, 5));
  B:= Copy(A);
  Button4.Caption:= IntToStr(B[1, 1]);
end;
Serg
+4  A: 

Like Serg and Andreas said, the _ routines all use compiler magic to provide functionality, so you should use Copy instead of _CopyArray, is instead of _IsClass, etc.

To directly answer your question though, no, there is no way to call those routines from Delphi code in other units. The makefile for the RTL passes an undocumented compiler switch when compiling System.pas and SysInit.pas which tells the compiler to convert any leading _ characters to @. _CopyArray becomes @CopyArray, for example. You can call it using a BASM (assembly) block, but that's it.

Craig Peterson
This is the solution (see question) Thank you very much!
Ritsaert Hornstra