Jump to content
Andre1

Access violation with move command

Recommended Posts

Hi, 
this is a simplified example. I need to do generic calling. In procedure Main, while calling the Move command,
an access violation is triggered. Why is the ByteArray not sucessfully filled?

 

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Rtti;

type
  TMidiInputEvent = packed record
    Timestamp: Integer;
    Status: Byte;
    Data1: Byte;
    Data2: Byte;
  end;

  TMyObject = class
  public
    procedure TargetMethod(p: PPointer; len: PInteger);
    procedure CallMethodByName(const AMethodName: string; p: PPointer; len: PInteger);
  end;

procedure TMyObject.TargetMethod(p: PPointer; len: PInteger);
var
  EventCount: Integer;
  Events: TArray<TMidiInputEvent>;
  TotalSize: NativeUInt;
  I: Integer;
begin
  SetLength(Events, 5); // 5 sample events
  for I := 0 to High(Events) do
  begin
    Events[I].Timestamp := 1000 + I * 100;
    Events[I].Status := $90; // Note On
    Events[I].Data1 := 60 + I; // MIDI note number
    Events[I].Data2 := 100; // Velocity
  end;
  EventCount := 5;

  TotalSize := EventCount * SizeOf(TMidiInputEvent);
  GetMem(p^, TotalSize);
  Move(Events[0], p^, TotalSize);
  len^ := TotalSize;
end;

procedure TMyObject.CallMethodByName(const AMethodName: string; p: PPointer; len: PInteger);
var
  RttiContext: TRttiContext;
  RttiType: TRttiType;
  RttiMethod: TRttiMethod;
  Args: TArray<TValue>;
begin
  RttiContext := TRttiContext.Create;
  try
    RttiType := RttiContext.GetType(Self.ClassType);
    RttiMethod := RttiType.GetMethod(AMethodName);

    if Assigned(RttiMethod) then
    begin
      SetLength(Args, 2);
      Args[0] := TValue.From<PPointer>(p);
      Args[1] := TValue.From<PInteger>(len);

      RttiMethod.Invoke(Self, Args);
    end
    else
    begin
      WriteLn(Format('Method ''%s'' not found.', [AMethodName]));
    end;
  finally
    RttiContext.Free;
  end;
end;


procedure Main;
var
  MyObj: TMyObject;
  Data: Pointer;
  DataSize: Integer;
  ByteArray: TBytes;
  TotalBytes: Integer;
begin
  MyObj := TMyObject.Create;
  try
    MyObj.CallMethodByName('TargetMethod', @Data, @DataSize);

 if (Data <> nil) and (DataSize > 0) then
    try
      TotalBytes := DataSize;
      SetLength(ByteArray, TotalBytes);
      // -------> Error occurs here
      Move(Data^, ByteArray[0], TotalBytes);
    finally
      FreeMem(Data);
    end;
  finally
    MyObj.Free;
  end;
end;

begin
  try
    Main;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Kind regards

André

Share this post


Link to post

Hi,

I found the issue, it needs to be:

 

Move(Data, ByteArray[0], TotalBytes);


 

Kind regards
André

 

Share this post


Link to post
17 hours ago, Andre1 said:

I found the issue, it needs to be:


Move(Data, ByteArray[0], TotalBytes);

 

That is wrong. Your original call was correct. It is your Move() call inside of TargetMethod() that was wrong to begin with. It needs to be this instead:

Move(Events[0], p^^, TotalSize);

Inside of TargetMethod():
 

- p is a pointer to Main's Data variable.

- p^ refers to the Data variable itself.

- p^^ refers to the memory block that the Data variable is pointing at.

 

The 2nd parameter of Move() wants the address of the memory block, but you are giving it the address of the Data variable instead. Thus, this call to Move() writes to the call stack of Main(), corrupting it. That is why the 2nd call to Move() inside of Main() crashes when it tries to read from memory being pointed at by the Data variable which resides in the corrupted call stack. You likely overwrote the Data variable during the 1st Move.

 

Imagine how your Main() would need to use its Data and DataSize variables if it didn't have to pass them to TargetMethod() at all:

procedure Main;
var
  Data: Pointer;
  DataSize: Integer;
  ByteArray: TBytes;
  TotalBytes: Integer;
begin
  ...
  GetMem(Data, ...); // <-- No ^ here
  Move(..., Data^, ...); // <-- 1 ^ here
  DataSize := ...; // <-- No ^ here
  ...
  if (Data <> nil) and (DataSize > 0) then
  try
    TotalBytes := DataSize;
    SetLength(ByteArray, TotalBytes);
    Move(Data^, ByteArray[0], TotalBytes); // <-- 1 ^ here
  finally
    FreeMem(Data);
  end;
  ...
end;

Now, because you added @ to Data and DataSize when passing them to TargetMethod(), you need an extra ^ on all accesses to Data and DataSize inside of TargetMethod():

GetMem(p^, TotalSize); // <-- 1 ^ here
Move(..., p^^, ...); // <-- 2 ^ here
len^ := ...; // <-- 1 ^ here

 

Edited by Remy Lebeau

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×