Delphiで実現する変長関数ノート


前にネットのコレクションから一部の変長パラメータ関数についてのメモを添付しました.どうせレジャーは大丈夫なので,Delphiを用いてC言語におけるPrintfのような任意の複数のパラメータを伝達できる関数を実現する方法を専門的に研究した.これは実際には難しくありません.関数の呼び出しルールをよく知っていれば、簡単です.実際にはこれが長くなり、全体的には、跡があるはずです.この兆候はどこにあるのか、キーです.つまり、この関数がどれだけのパラメータを伝達しているのかを知ることが最も重要です.CのprintfとWindowsのwsPrintfを参照すると、実際にはFormatのパラメータタイプがあることがわかります.このタイプがあれば、このフォーマットパラメータに基づいて、中に何個の%d,%s,%fというマッチング内容があるかを取得することができ、これにより、関数がどれだけのパラメータを伝達しているかを知ることができます.それから、私たちはプログラムスタックの中からこれらのパラメータを一つ一つ探し出して、それから私たちの関数の中で処理することができます.では、パラメータがスタックの中にあるのはなぜかと聞かれるに違いありません...これ..やはり関数呼び出しの法則に帰して言えば、最初に制定された時、stdcall、pascal、DelphiのデフォルトのRegister呼び出しはすべてプログラムによってスタックを自動的に管理することを規定して、それ以外に、もう一つの呼び出し方式はcdeclという方式で、私たち自身がスタックをバランスさせて、自分で管理して、動的な個数のパラメータの関数で、このcdeclの呼び出し方式でなければなりません.この呼び出し方式では、パラメータは右から左へスタックに押し込まれます.このように設計するのは理にかなっています.右から左へ押し込むと、早く入るパラメータほど遅く押し込まれます.そうすれば、最も役に立つ実際のパラメータを知ることができます.そうすれば、長くなる関数に入ると、私たちは知ることができます.現在のスタックのスタックトップが私たちの現場であり,さらにネット上で再帰すると,最初のパラメータ,すなわち[ebp+8]が現在の最初のパラメータである.これにより、2番目のパラメータ、3番目のパラメータが見つかります....
したがって,最初のパラメータでは,後に実際にどれだけの変形パラメータが伝達されたかを取得しなければならない.その後、スタックを通じて、一つ一つ見つけて、関数で処理することができます.
こんなにたくさん話して例を挙げて実験してみた.たとえば、関数を書くには、入力された数値の和を取得する必要があります.
function GetSum(NumCount: integer{N1,N2: integer}): Integer;cdecl;
asm
  {
  push ebp
  mov ebp,esp
  }
  mov ecx,[ebp + 8];//         ,         
  //        
  mov edx,12
  mov eax,0
@@add:
  add eax,[ebp].edx
  add edx,4
  loop @@add
end;

次のように呼び出されます.
type
  VA_Sum = function(NumCount: integer{N1,N2}): Integer; cdecl varargs;
 
var
  m: VA_Sum;
begin
  m := GetSum;
  ShowMessage(inttostr(m(4,3,4,5,6)));
end;

たとえば、文字列関数の接続
function ConStr(NumCount: integer{N1,N2}): string;cdecl;
var
  st1: string;
  i: Integer;
begin
  asm
    mov esi,16
  end;
  for i := 0 to NumCount - 1 do
  begin
    asm
      mov edi,[ebp].esi
      cmp edi,128
      jb @@char
      mov DWORD ptr st1,edi
      @@Char:
      add esi,4
    end;
    Result := Result + st1;
  end;
end;
//    

type
  VA_ConStr = function(NumCount: integer{N1,N2}): string; cdecl varargs;
 
var
  cstr: VA_ConStr;
begin
  cstr := constr;
  ShowMessage(cstr(3,'Test','   ','  '));
end;