코딩 님이 쓰신 글 :
: procedure f(var v);
: begin
:
: end;
:
: procedure TForm1.Button1Click(Sender: TObject);
: var
: str: string;
: i: integer;
: begin
: i := 777;
: str := 'hello';
: f(i);
: f(str);
: end;
:
:
: v가 타입 없는 var로 되어 있는데요
:
: 프로시져 f 에서 v 의 타입이 string 인지 integer 인지 알수 있는 방법이 있을까요?
:
: 델마당에 올라온 질문인데요
: 제 생각으론 아무리 생각해봐도 불가능 할것 같긴한데요
: 혹시나 해서 여쭈어 봅니다.
:
답변:
도움말만 봐도 알수 있는 단순한 컴포넌트 사용법 관련된 글이면 한번 읽고 지나쳤을 텐데...
기술적인 부분이라 답변 글 남깁니다.
CPU의 고유한 ID 값을 알아내기 위해 메인보드의 롬바이오스 SMB Bios 펌웨어 테이블 파싱이 필요하고
펌웨어 테이블의 자료구조 상... 테이블 파싱을 위해 T** two pointer 타입의 포인터 연산이 자주 사용되는데
C++에선 T1 ** 타입을 T2 *& 으로 포인터에 대한 레퍼런스 타입으로 치환해서 포인터 연산을 간략하게 할 수 있으나
T1 ** p;
T2 v = *((T2 *&)p)++;
T** two 포인터 연산을 파스칼로 작성하면 코드가 지져분해지므로 속편하게 예제는 어셈블리 코드로 작성 합니다.
Object Inspector 를 통해 다양한 타입을 참조할 수 있는데... Boolean 타입을 예로 들어 봅시다.
부울형의 경우 True, False 값을 Object Inspector 에서 표시해 주죠.
이와 같은 메카니즘이 가능한 것은 델파이 컴파일러에 의해서 Rtti 정보가 바이너리로 Emit 되기 때문인데요.
(Object Inspector를 통해서 보여지는 타입 정보들은 컴파일러에 의해 바이너리로 Emit 된 Rtti 정보 중에서
published 된 극히 일부분에 불과함)
Boolean 타입의 정보를 런타임 중에 바이너리에서 직접 찾아서 표시하는 예를 들어 봅시다.
function GetBooleanType(): string;
type PS = PShortString;
var tn, v1, v2: DWORD;
begin
asm
mov esi, $1000
push 0
call GetModuleHandle
add esi, eax
mov esi, [esi]
lea esi, [esi + 1]
mov tn, esi
mov al, [esi]
movzx eax, al
lea esi, [eax + esi + 14]
mov v1, esi
mov al, [esi]
movzx eax, al
lea esi, [eax + esi + 1]
mov v2, esi
end;
Result := 'typeName: ' + PS(tn)^ + ' ,v1: ' + PS(v1)^ + ' ,v2: ' + PS(v2)^;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Lines.Add( GetBooleanType() );
end;
위의 경우는 Boolean 타입 하나만 예로 든 거고...
class, record, virtual method, propery, message handler, event handler, class method, interface method 등등
델파이로 컴파일 되어있는 바이너리의 모든 타입 정보들을 리버스 엔지니어링으로 바이너리로 부터 직접 알아 낼수 있지요.
그리고...
procedure f(var v);
begin
end;
procedure TForm1.Button1Click(Sender: TObject);
var
str: string;
i: integer;
begin
i := 777;
str := 'hello';
f(i);
f(str);
end
procedure f(var v); 에서 v 의 타입을 알아내는 건...
컴파일러에 의해서 타입 정보가 넘어오지 않아서 일견 보기에는 불가능해 보일 수도 있으나
넘어온 파라미터 v 가 string 과 integer 둘 중에 하나라는 조건이 주어지고
델파이 컴파일러 내부구조를 알고 있으면 이 또한 가능 합니다.
procedure Foo(var V);
var mi: array[0..27] of Byte;
begin
asm
mov esi, V
mov esi, [esi]
lea esi, [esi - 12];
lea edi, mi
push 28
push edi
push esi
call VirtualQuery
mov [edi + 16], eax
and eax, eax
je @skip
add esi, 12
and dword ptr [edi + 20], $24
mov dword ptr [edi + 20], esi
je @next
cmp dword ptr[esi - 12], $204b0
jnz @next
mov byte ptr[edi + 16], 2
jmp @skip
@next:
mov byte ptr[edi + 16], 1
@skip:
end;
if mi[16] = 1 then
Form1.Memo1.Lines.Add(Format('type: integer, value: %d', [PInteger(@mi[20])^]))
else if mi[16] = 2 then
Form1.Memo1.Lines.Add(Format('type: string, value: %s', [PChar(PDword(@mi[20])^)]))
else
Form1.Memo1.Lines.Add('Fail: VirtualQuery()')
end;
procedure TForm1.Button1Click(Sender: TObject);
var
s: string;
i: integer;
begin
i := 777;
s := 'hello';
Foo(i);
Foo(s);
end;
모든 코드는 Rad studio 10.3.1 기준으로 작성.