Andre1 0 Posted 20 hours ago 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
Andre1 0 Posted 20 hours ago Hi, I found the issue, it needs to be: Move(Data, ByteArray[0], TotalBytes); Kind regards André Share this post Link to post
Remy Lebeau 1622 Posted 2 hours ago (edited) 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 2 hours ago by Remy Lebeau Share this post Link to post