2013-01-22 33 views
16

我正在尋找方法來獲取Delphi記錄中字段的偏移量。以下兩種方法可行,但我希望有一個更清晰的方法。基本上我會喜歡第三個showmessage工作。有任何想法嗎?Delphi:記錄字段的偏移量

編輯: 好的,下面的答案正常工作,謝謝!作爲參考,這裏的彙編輸出的各種選項:

---- result:=longint(@abc.c)-longint(abc); ---- 
lea edx,[eax+$08] 
sub edx,eax 
mov eax,edx 

---- mov eax,offset rec_a.c ---- 
mov eax,$00000008 

---- result:=longint(@rec_a(nil^).c); ---- 
xor eax,eax 
add eax,$08 

EDIT2:看起來這是前一個問題的重複:previous similar question由RRUZ如下所述。如上所示,另一種方法是聲明一個全局變量並按如下所示使用它。奇怪的是,編譯器仍然不能在彙編輸出中看到正確的值,所以爲了提高效率和可讀性,最好使用nil方法。

---- var ---- 
---- rec_a_ofs:rec_a; ---- 
---- ... ---- 
---- result:=longint(@rec_a_ofs.c)-longint(@rec_a_ofs); ---- 
mov eax,$0045f5d8 
sub eax,$0045f5d0 

edit3:好吧,修改後的代碼與所有已知的方法來完成此操作。請注意,爲3rd,4th和5th(類方法)生成的彙編代碼是相同的,無論它們是否內聯。當你做這些事情時,選擇你最喜歡的方式!

type 
prec_a=^rec_a; 
rec_a=record 
    a:longint; 
    b:byte; 
    c:pointer; 

    class function offset_c:longint;static;inline; 
end; 

//const 
// rec_a_field_c_offset=longint(@rec_a(nil^).c); // no known way to make this work 

{$warnings off} 
function get_ofs1:longint;inline; 
var 
abc:^rec_a; 
begin 
result:=longint(@abc.c)-longint(abc); 
end; 
{$warnings on} 

function get_ofs2:longint; 
asm 
mov eax,offset rec_a.c 
end; 

function get_ofs3:longint;inline; 
begin 
result:=longint(@rec_a(nil^).c); 
end; 

function get_ofs4:longint;inline; 
begin 
result:=longint(@prec_a(nil).c); 
end; 

class function rec_a.offset_c:longint; 
begin 
result:=longint(@prec_a(nil).c); 
end; 

var 
rec_a_ofs:rec_a; 

function get_ofs6:longint;inline; 
begin 
result:=longint(@rec_a_ofs.c)-longint(@rec_a_ofs); 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
showmessage(inttostr(get_ofs1)); 
showmessage(inttostr(get_ofs2)); 
showmessage(inttostr(get_ofs3)); 
showmessage(inttostr(get_ofs4)); 
showmessage(inttostr(rec_a.offset_c)); 
showmessage(inttostr(get_ofs6)); 
// showmessage(inttostr(rec_a_field_c_offset)); 
end; 
+0

BASM代碼中不需要'offset'關鍵字;只是'mov eax,rec_a.c'。 – kludg

+1

[獲取結構變量的位置](http:// stackoverflow。com/questions/13168959/get-position-of-a-struct-var) – RRUZ

+0

@serg甚至冗餘代碼可能會增加soem安全性 - 消除你沒有預料到的歧義性或者在錯誤輸入和更寬鬆的編碼時造成語法錯誤將編譯錯誤的結果 –

回答

17

我一直用這個辦法:

Offset := Integer(@rec_a(nil^).c); 

不要讓使用nil^把你趕走,這是完全安全的。不要擔心64位指針截斷。如果你有一個大於4GB的記錄,那麼你有更大的問題!

+0

感謝大衛的快速回答。 – Marladu

+0

太棒了!與我的ASM背景我從來沒有想過... ...恥辱... 你試圖使它爲常量? –

+0

@Arioch我不認爲你可以做一個字段偏移量的const表達式 –

2

你也可以使用一個通用的方法:

uses 
    System.SysUtils,TypInfo,RTTI; 

function GetFieldOffset(ARecordTypeInfo : PTypeInfo; 
         const ARecordFieldName : String) : Integer; 
var 
    MyContext: TRttiContext; 
    MyField: TRttiField; 
begin 
    if (ARecordTypeInfo.Kind <> tkRecord) then 
    raise Exception.Create('Not a record type'); 
    for MyField in MyContext.GetType(ARecordTypeInfo).GetFields do 
    if MyField.Name = ARecordFieldName then 
    begin 
     Exit(MyField.Offset); 
    end; 
    raise Exception.Create('No such field name:'+ARecordFieldName); 
end; 

,並調用它是這樣的:

ShowMessage(IntToString(GetFieldOffset(TypeInfo(rec_a),'c'))); 

沒有那麼快,因爲你的其他選擇,但給出了一個統一的通用的解決方案。你的選擇


尋找這裏乾淨的解決方案,它似乎是最好的是聲明一個泛型函數:

function GetFieldOffset(const P : Pointer) : Integer; Inline; 
// Example calls : 
// GetFieldOffset(@PMyStruct(nil).MyParameter); 
// GetFieldOffset(@TMyStruct(nil^).MyParameter); 
begin 
    Result := Integer(P); 
end; 

所以即使調用看起來很笨拙,功能名稱會告訴你這是怎麼回事上。內聯調用消除了函數調用的開銷,因此它將作爲代碼美化工具。

有可能獲得持續的價值爲創紀錄的基地和場地址:

const 
    cStruct : MyStruct =(); 
    cMyInteger3Offs : Pointer = @cStruct.MyInteger3; 
    cMyStructBase : Pointer = @cStruct; 

但是這不會使代碼看起來更乾淨。

+0

這種方法也放棄了編譯時類型檢查和IntelliSense支持。 –

+0

@UliGerhardt,是的,有時使用rtti就是這種情況。我在尋找一個解決方案,'c'可以在編譯時解決,但沒有找到簡單的方法。 –

+0

您是否發現了困難的方式?我比我想象的要多得多,仍然無法找到一種方法來將常量中的記錄字段偏移。對於這個答案中顯示的方法還要注意的是,它只適用於較新版本的delphi,舊版本沒有單位rtti。 – Marladu

相關問題