문제

이 질문에 기초 이전, 지만,그냥 참고하시기 바랍니다.

나리,그러나,내가 찾은 뭔가가 분명하지 않 백성아 나를 앙망하라 그리하면 누구나 설명할 수 있는 다음과 같은 행동을,그것은으로 인해 발생하는 것으로 나타났다

나는 다음과 같은 클래스:

type
  TMyObj = class
  published
    procedure testex(const s: string; const i: integer);
  end;

procedure TMyObj.testex(const s: string; const i: integer);
begin
  ShowMessage(s + IntToStr(i));
end;

다음과 같은 두 가지 절차:

procedure CallObjMethWorking(AMethod: TMethod; const AStrValue: string; const AIntValue: Integer);
begin
  asm
    PUSH DWORD PTR AIntValue;
    PUSH DWORD PTR AStrValue;
    CALL AMethod.Code;
  end;
end;

procedure CallObjMethNOTWorking(AInstance, ACode: Pointer; const AStrValue: string; const AIntValue: Integer);
begin
  asm
    MOV EAX, AInstance;
    PUSH DWORD PTR AIntValue;
    PUSH DWORD PTR AStrValue;
    CALL ACode;
  end;
end;

를 테스트하기 위해 버전 작업,하나의 요구하는 다음을 호출합니다:

procedure ...;
var
  LObj: TMyObj;
  LMethod: TMethod;
  LStrVal: string;
  LIntVal: Integer;
begin
  LObj := TMyObj.Create;
  try
    LMethod.Data := Pointer( LObj );
    LMethod.Code := LObj.MethodAddress('testex');

    LStrVal := 'The year is:' + sLineBreak;
    LIntVal := 2012;

    CallObjMethWorking(LMethod, LStrVal, LIntVal);
  finally
    LObj.Free;
  end; // tryf
end;

하 테스트 버전을 사용:

procedure ...;
var
  LObj: TMyObj;
  LCode: Pointer;
  LData: Pointer;
  LStrVal: string;
  LIntVal: Integer;
begin
  LObj := TMyObj.Create;
  try
    LData := Pointer( LObj );
    LCode := LObj.MethodAddress('testex');

    LStrVal := 'The year is:' + sLineBreak;
    LIntVal := 2012;

    CallObjMethNOTWorking(LData, LCode, LStrVal, LIntVal);
  finally
    LObj.Free;
  end; // tryf
end;

마지막으로 질문:지 않는 이유는 무엇 CallObjMethNOTWorking 작업 동안, CallObjMethWorking 입니까?나는 거기에 특별한 무언가가 어떻게 컴파일러 TMethod...하지만 이후 내 어셈블리 기술은 제한된,나는 그것을 이해할 수 없다.

내가 매우 감사할 수 있는 사람이 있는 이것을 설명하 나,당신을 감사하십시오!

도움이 되었습니까?

해결책

Delphi Win32의 기본 호출 규칙은 "레지스터"입니다.첫 번째 매개 변수는 EAX로 전달되고 EDX의 두 번째 및 ECX의 세 번째가 전달됩니다.스택은 3 개 이상의 매개 변수가있는 경우에만 사용되거나 4 바이트보다 큰 값 유형이 전달되지만 예제의 경우가 아닙니다.

컴파일러가 에 collobjmethworking이 호출되었을 때 ECX의 AINTValue를 에 배치했기 때문에 첫 번째 callobjmethworking 프로 시저가 작동합니다.그러나 두 푸시 지침을 정리하지 않으므로 절차가 반환되면 잘못된 것들이 발생합니다.

코드는 이렇게 보입니다.이 경우 STDCALL 지시문은 선택 사항이지만 실제로 메서드를 실제로 호출하기 전에 다른 목적을 위해 레지스터를 사용하기 때문에 매개 변수가 손실되지 않도록하는 것이 좋습니다.

procedure CallObjMeth(AInstance, ACode: Pointer; const AStrValue: string; const AIntValue: Integer); stdcall;
asm
  MOV EAX, AInstance;
  MOV EDX, DWORD PTR AStrValue;
  MOV ECX DWORD PTR AIntValue;
  CALL ACode;
end;
.

다른 팁

Henrick Hellström 가 올바른 그 응답, 고,나는 당신의 질문에 태그가 있으로 2010 년 델파이고,따라서만 관심 Win32.그러나,당신은 관심이있을 수 있습니다 무엇을 보고 상황을 것처럼 보이는 경우 앞으로 이동하 Win64(델파이>=XE2),그래서 나는 추가 예 Win64 버전을 Henrick 의 코드:

procedure CallObjMeth(AInstance, ACode: Pointer; const AStrValue: string; const AIntValue: Integer); stdcall;
asm
{$IFDEF CPU386}
  MOV EAX, AInstance;
  MOV EDX, DWORD PTR AStrValue;
  MOV ECX, DWORD PTR AIntValue;
  {$IFDEF MACOS}
   //On MacOSX32 ESP = #######Ch here       
   SUB ESP, 0Ch  
  {$ENDIF}     
  CALL ACode;
  {$IFDEF MACOS}
   ADD ESP, 0Ch // restoring stack
  {$ENDIF}     
{$ENDIF}
{$IFDEF CPUX64}{$IFDEF WIN64} // <- see comments
  .NOFRAME //Disable stack frame generation
  //MOV RCX, AInstance {RCX} //<- not necessary because AInstance already is in RCX
  MOV R10, ACode {RDX}
  MOV RDX, AStrValue {R8}
  MOV R8D, AIntValue {R9D}
  SUB RSP, 28h    //Set up stack shadow space and align stack: 4*8 bytes for 4 params + 8 bytes bytes for alignment
  {$IFNDEF DO_NOT_TEST_STACK_ALIGNMENT}
  MOVDQA XMM5, [RSP]  //Ensure that RSP is aligned to DQWORD boundary -> exception otherwise
  {$ENDIF}
  CALL R10 //ACode
  ADD RSP, 28h  //Restore stack
{$ENDIF}{$ENDIF}
end;

거기에 여러 해 만들기 위하여:

1) ASM 문의:델파이 XE2 64 없는 혼합의 파스칼과 asm 코드,그래서를 작성하는 방법이 어셈블리 코드에서는 일상적인으로 구성된 단일 asm..end 블록이 없 begin..end.Note begin..end 의 주위에 당신의 32 비트 어셈블리 코드 또는 효과가 있습니다.특히,당신은 당신을 강제로세대의 스 프레임 및 컴파일러 지역의 사본 기능 매개 변수입니다.(있을 경우 당신은 리조트를 사용하여 어셈블리는 첫 번째 장소에서,당신은 원하지 않을 수도 컴파일러습니다.)

2)호출 convention:에 Win64,하나만 있소.같은 것들 registerstdcall 을 효과적으로 의미가 없;그것은 모두 같은, Microsoft Win64 화 convention.그것은 본질적으로 이:매개 변수가 전달된 RCX, RDX, R8R9 레지스터(그리고/또는 XMM0-XMM4, 반환 값 RAX/XMM0.보다 큰 64 비트 값이를 통해 전달되 참조.

라는 기능을 사용할 수 있: RAX, RCX, RDX, R8-R11, ST(0)-ST(7), XMM0-XMM5, YMM0-YMM5, YMM6H-YMM15H, 고 유지해야 합니다 RBX, RSI, RDI, RBP, R12-R15, XMM6-XMM15.적절한 경우,이라는 기능을 발행 할 필요가 CLD/EMMS/VZEROUPPER 를 CPU 예상되는 상태입니다.

3)선형 공간과 그림자 중요한 것은,각각의 기능은 그것의 자신의 그림자 공간에서 스택은 적어도 4QWORD param 의 가치가 스택의 공간이 없는 경우에도 params 및 여부에 관계없이라는 기능을 실제로 접촉니다.또한,사이트에서의 각 기능 통화(에서 각 CALL 서), RSP 이 될 것으로 예상되는 16 바이트의 정렬(에 대해 동일 ESP 에 MacOSX32,btw.).이 종종 같은 것들: sub rsp, ##; call $$; add rsp, ## 을 구성하는##것 합계(QWORD)매개변수는 함수가 호출 될 것,플러스 선택 사항은 8 바이트의 정렬 RSP.참고로의 정렬 RSPCALL 사이트에서 결과 RSP = ###8h 에 따라 기능 항목(기 CALL 두고 반환 주소에 스택),그래서 가정에 아무도 놨 RSP 기 전에,당신은 그것을 기대할 수 있습니다날 것입니다.

예제에서 제공하 SSE2 MOVDQA 명령을 사용하여 테스트의 정렬 RSP. (XMM5 으로 사용된 대상 등록하기 때문에 그것을 자유롭게 수정할 수 있습니다 아직 포함할 수 없습니다 모든 기능 매개 변수 데이터).

4)정 여기에 코드에서는 컴파일러가 삽입하지 않을 변경하는 코드 RSP.상황이 발생할 수도 있습니다 이 사실이 아닐 수 있습니다,그래서 조심이 가정입니다.

5)예외 처리 에서 예외 처리 Win64 는 조금 복잡하고 제대로 수행하여 컴파일러(위의 예제 코드는 이것을 하지 않).을 허용하는 컴파일러 그렇게,이상적으로 귀하의 코드를 사용해야 합니다 새로운 BASM 지시/의사 지침 .PARAMS, .PUSHNV.SAVENV 로에 의해 설명 여기에국어.주(틀)상황이 나쁜 일이 일어날 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top