Source file: /~heha/messtech/calldll.zip/SRC/calldll.pas

library calldll;	{32-bit-Version!}
{$HINTS ON}
{$Z4}
{$D Matlab-MEX: Universal-DLL-Aufrufschnittstelle (h#s 03/01)}

uses PasMex32,Windows;

{Schlafmützen, die die Delphi-Standardbibliothek geschrieben haben:}
function wsprintf1(ret,template,a1: PChar):LongInt;
 cdecl; external 'user32.dll' name 'wsprintfA';
function wsprintf2(ret,template,a1,a2: PChar):LongInt;
 cdecl; external 'user32.dll' name 'wsprintfA';

var
 hDllLast: THandle;		{geladene DLL}
 ParseStr: array[0..1023] of Char;
 ProcEntry: TFarProc;
 cdecl: Boolean;		{sonst stdcall!}

type
 PAnyNum=^TAnyNum;
 TAnyNum=record
 case integer of
  1: (x: double);
  2: (s: single);
  3: (l: LongInt);
  4: (w: Word);
  5: (i: SmallInt);
  6: (c: Char);
  7: (b: Byte);
  8: (h: ShortInt);
 end;

const
 ARG_I08	=$81;
 ARG_I16	=$82;	{Standard für mxIsNumeric}
 ARG_I32	=$84;
 ARG_U08	=$01;
 ARG_U16	=$02;
 ARG_U32	=$04;
 ARG_F32	=$C4;
 ARG_F64	=$C8;
 ARG_C08	=$01;	{Standard für mxIsString}
 ARG_Str	=$00;
 ARG_Ptr	=$20;	{falls m*n<>1}
 ARG_Ret	=$10;
{Bit
 7=vzb/vzl
 6=Gleitkomma/Ganzzahl
 5=Pointer auf Datentyp (Indirektion, immer FAR)
 4=Parameter-Rückgabe (schließt Bit 5 ein)
 3..0=Zahl der Bytes pro Element}
var
 argt: array[0..17] of Byte;
{Index 0: Funktionsergebnis, 1..17: Argumente}
 allocs: array[0..17] of PAnyNum;
 sizes: array[0..17] of LongInt;
type
 TType=record
  t: Byte;
  c: Char;
  s: array[0..3] of Char;
 end;

const
 CType: array[0..8] of TType=(
  (t: ARG_I08; c: 'h'; s: 'i08'),
  (t: ARG_I16; c: 'i'; s: 'i16'),
  (t: ARG_I32; c: 'l'; s: 'i32'),
  (t: ARG_U08; c: 'c'; s: 'u08'),
  (t: ARG_U16; c: 'u'; s: 'u16'),
  (t: ARG_U32; c: 'w'; s: 'u32'),
  (t: ARG_F32; c: 'f'; s: 'f32'),
  (t: ARG_F64; c: 'g'; s: 'f64'),
  (t: ARG_Str; c: 's'; s: 'str'));	{nullterminierter String}

function mxGetMxN(pm: PArray):LongInt;
 begin
  mxGetMxN:=mxGetM(pm)*mxGetN(pm);
 end;

function lstrchr(s:PChar; c:Char):PChar;
 begin
  lstrchr:=nil;
  while s^<>#0 do begin
   if s^=c then begin
    lstrchr:=s;
    exit;
   end;
   s:=AnsiNext(s);
  end;
 end;

procedure ParseArg(Arg:PChar; ArgNum: Integer);
{Einen einzelnen Argument-Prototypen erfassen}
(* Syntaxdiagramm: {*}spec{[{arraysize}]} *)
 var
  sp,sp2: PChar;
  i,e: Integer;
 begin
  while Arg^=' ' do Inc(Arg);		{TrimLeft}
  if Arg^='*' then begin
   Inc(Arg);
   argt[ArgNum]:=argt[ArgNum] or ARG_Ptr	{Indirektion}
  end;
  sp:=lstrchr(Arg,'[');
  if sp<>nil then begin
   sp^:=#0;
   inc(sp);
   argt[ArgNum]:=argt[ArgNum] or (ARG_Ptr or ARG_Ret);	{Indirektion und Ret}
   sp2:=lstrchr(sp,']');
   if sp2=nil then mexErrMsgTxt('Prototyp-[]');
   sp2^:=#0;
   if sp2<>sp then begin		{sonst Länge von Matrix nehmen}
    Val(sp,sizes[ArgNum],e);
    if e<>0 then mexErrMsgTxt('Prototyp-Arraygr÷▀e');
   end;
  end;
  for i:=0 to 8 do with CType[i] do begin
   if (Arg[0]=c) and (Arg[1]=#0)
   or (lstrcmpi(Arg,s)=0) then begin
    argt[ArgNum]:=argt[ArgNum] and (ARG_Ptr or ARG_Ret) or t;
    exit;
   end;
  end;
  mexErrMsgTxt('Prototyp-Typ');
 end;

function IsBadHandle(h:THandle):Boolean;
 begin
  IsBadHandle:=(h>=0) and (h<=32);
 end;

procedure ParseString;
 var
  hDLL: THandle;
  sp,sp2: PChar;
  wordval: Word;
  arg,e: Integer;
  s,sDLL: array[byte] of Char;	{zu ladende DLL}
  OldEM: Word;
 begin
  sp:=lStrChr(ParseStr+2,':');	{<Laufwerk:> übergehend}

  if sp<>nil then begin		{Versuch, die DLL zu laden}
   lstrcpyn(sDLL,ParseStr,sp-ParseStr+1);
   sp2:=sDLL+lstrlen(sDLL);	{End-Position}
   OldEM:=SetErrorMode(SEM_NoOpenFileErrorBox);
   hDLL:=LoadLibrary(sDLL);
   if IsBadHandle(hDLL) then begin
    lstrcpy(sp2,'.DLL');	{heranflicken (1. Versuch)}
    hDLL:=LoadLibrary(sDLL);
    if IsBadHandle(hDLL) then begin
     lstrcpy(sp2,'32.DLL');	{heranflicken (2. Versuch)}
     hDLL:=LoadLibrary(sDLL);
    end;
   end;
   SetErrorMode(OldEM);
   if not IsBadHandle(hDLL) then begin
    FreeLibrary(hDllLast);
    hDllLast:=hDLL;
   end else begin
    wsprintf2(s,'Kann DLL "%s" nicht finden/laden, Code=%d',sDLL,PChar(hDLL));
    mexErrMsgTxt(s);
   end;
   inc(sp);
  end else sp:=ParseStr;
  sp2:=lstrchr(sp,' ');
  if sp2<>nil then sp2^:=#0;
  cdecl:=false;
  Val(sp,wordval,e);		{numerischer Einsprung?}
  if e=0 then sp:=MakeIntResource(wordval)
  else if sp^='_' then cdecl:=true;
  ProcEntry:=GetProcAddress(hDllLast,sp);
  if (ProcEntry=nil) and (LongRec(LongInt(sp)).hi<>0) then begin
   lstrcpy(sDLL,sp);
   lstrcat(sDLL,'A');	{ASCII-Version probieren (wird gern vergessen)}
   ProcEntry:=GetProcAddress(hDllLast,sDLL);
  end;
  if ProcEntry=nil then begin
   if LongRec(LongInt(sp)).hi=0 then begin
    wsprintf1(s,'Kann Einsprungpunkt #%d nicht finden',sp);
   end else begin
    wsprintf1(s,'Kann Einsprungpunkt "%s" nicht finden',sp);
   end;
   mexErrMsgTxt(s);
  end;
  arg:=1;
  while sp2<>nil do begin
   sp:=sp2+1;
   if sp^=#0 then break;	{Nichts mehr zu parsen}
   sp2:=lstrchr(sp,'=');
   if sp2<>nil then arg:=0	{Retval-Argument}
   else sp2:=lstrchr(sp,',');
   if sp2<>nil then sp2^:=#0;
   ParseArg(sp,arg);
   Inc(arg);
  end;
 end;

procedure ShowProto(hirhs:Integer);
 var
  sp: PChar;
  carg: Byte;
  c: Char;
  i,j: Integer;
  s: array[0..255] of Char;
 begin
  sp:=s;
  for i:=0 to hirhs do begin
   carg:=argt[i] and not (ARG_Ptr or ARG_Ret);
   for j:=0 to 8 do if carg=CType[j].t then break;
   lstrcpy(sp,CType[j].s); Inc(sp,3);
   if argt[i] and ARG_Ptr <>0
   then Inc(sp,wsprintf1(sp,'[%d]',PChar(sizes[i])));
   if argt[i] and ARG_Ret <>0
   then begin sp^:='r'; inc(sp); end;
   c:=',';
   if i=hirhs then c:=#0;
   if i=0 then c:='=';
   sp^:=c; inc(sp);
  end;
  sp^:=#0;
  sp:=s;
  mexPrintf1('%s'#10,LongInt(sp));
 end;

function CallEntry(hirhs:Integer; var prhs:TMatArr):Double; stdcall;
 const
  StackFehler: array[0..11] of Char='Stackfehler';
 var
  i: integer;
  xx: TAnyNum;
  xp,xp2: PAnyNum;
  rp: PDouble;
  l: LongInt;
  carg: Byte;
  cmat: PArray;
  csiz: LongInt;
{$IFOPT D+}
  s: array[0..15] of Char;
{$ENDIF}
  savestack,teststack: Cardinal;
 begin
  asm mov [savestack],esp; mov [teststack],esp end;
  for i:=hirhs downto 1 do begin {cdecl und stdcall: umgek. Reihenfolge!}
   carg:=argt[i];
   cmat:=prhs[i];
   csiz:=sizes[i];
   if carg and Arg_Ptr <>0 then begin
    carg:=carg and not (ARG_Ptr or ARG_Ret);
    if carg=ARG_Str then begin
     xp:=PAnyNum(LocalAlloc(LMEM_Fixed,csiz+1));
     if xp=nil then mexErrMsgTxt('Speicherfehler');
     allocs[i]:=xp;
     mxGetString(cmat,PChar(xp),csiz+1);
    end else if (carg=ARG_F64)
    and (mxGetMxN(cmat)=csiz)
    then PDouble(xp):=mxGetPr(cmat)	{nicht ganzes Array kopieren}
    else begin
     xp:=PAnyNum(LocalAlloc(GMEM_Fixed,csiz*(carg and $0F)));
     if xp=nil then mexErrMsgTxt('Speicherfehler');
     allocs[i]:=xp;
     rp:=mxGetPr(cmat);
     l:=mxGetMxN(cmat);
     xp2:=xp;
     if l>csiz then l:=csiz;
     repeat			{compiliert besser als FOR}
      with xp2^ do case carg of
       ARG_I08: h:=Round(rp^);
       ARG_I16: i:=Round(rp^);
       ARG_I32,
       ARG_U32: l:=Round(rp^);
       ARG_U16: w:=Round(rp^);
       ARG_U08: b:=Round(rp^);
       ARG_F32: s:=rp^;
       ARG_F64: x:=rp^;
      end;
      Inc(rp);
      Inc(PChar(xp2),carg and $0F);
      Dec(l);
     until l=0;
    end{case};
    asm	push [xp] end;
   end else begin
    xx.x:=mxGetScalar(cmat);
    case carg of
     ARG_F64: ;
     ARG_F32: xx.s:=xx.x;
     else     xx.l:=Round(xx.x);
	{Alle integralen Typen in INTEL-Notation: reicht!}
    end;
    asm	mov	al,[carg]
	and	al,0Fh
	cmp	al,8		{DOUBLEs?}
	jc	@@1
	push	dword ptr [xx+4]
@@1:	{alles andere sind LONGINTs, hier ganz einfach!}
	push	dword ptr [xx]
    end;
   end{if};
  end{for};
  asm	cmp	[cdecl],FALSE
	jz	@@pas
	mov	[teststack],esp	{bei cdecl ist dies die Stack-Rückgabepos.}
@@pas:	call	[ProcEntry]	{Aufruf!}
	mov	[xx.l],eax
	cmp	[teststack],esp	{steht richtig?}
	je	@@1
	push	offset stackfehler
	call	mexWarnMsgTxt		{ob dieser ST(0) in Ruhe läßt?}
	pop	cx		{Matlab-Funktionen sind cdecl!}
@@1:	mov	esp,[savestack]
	mov	al,byte ptr argt
	cmp	al,ARG_F64
	jz	@@sto
	cmp	al,ARG_F32
	jz	@@sto
	cmp	al,ARG_I16
	jz	@@i16
	cmp	al,ARG_I32
	jz	@@i32
	cmp	al,ARG_U32
	jz	@@i32
	cmp	al,ARG_U16
	jz	@@u16
	cmp	al,ARG_U08
	jz	@@u08
	cmp	al,ARG_I08
	jz	@@i08
	jmp	@@e
@@i08:	mov	al,[xx.h]
	cbw
	mov	[xx.i],ax
	jmp	@@i16
@@u08:	mov	byte ptr xx[1],0
@@i16:	fild	[xx.i]
	jmp	@@sto
@@u16:	mov	word ptr xx[2],0
@@i32:	fild	[xx.l]
	jmp	@@sto
@@sto:	fwait
	fstp	[@result]
	fwait
@@e:
  end;
  for i:=1 to hirhs do begin
   carg:=argt[i];
   cmat:=prhs[i];
   csiz:=sizes[i];
   if carg and ARG_Ret <>0 then begin
    carg:=carg and not (ARG_Ptr or ARG_Ret);
    if carg=ARG_Str then begin
     mexWarnMsgTxt('String-Rⁿckgabe fⁿhrt zu InstabilitΣt in Matlab');
     mxDestroyArray(cmat);
     cmat:=mxCreateString(PChar(allocs[i]));	{Ende an terminierender Null}
{     MessageBox(hwndMatlabCmd,Ptr(allocs[i],0),'Hier vorbei',0);}
{MATLAB scheint Schwierigkeiten zu haben, Return-Werte auf der rechten
 Seite (als quasi VAR-Parameter) zu liefern!}
     prhs[i]:=cmat;
    end else begin
     if mxGetMxN(cmat)<>csiz then begin
      mexWarnMsgTxt('Die rechtsseitige Ergebnismatrix sollte gleich gro▀ sein!'#10+
	'Matlab wird instabil');
      mxDestroyArray(cmat);
      cmat:=mxCreateDoubleMatrix(1,csiz,mxREAL);
      prhs[i]:=cmat;		{ob sie jemals da ankommt? Manchmal!}
     end else if carg=ARG_F64 then continue;	{nächstes I}
     rp:=mxGetPr(cmat);
     xp:=allocs[i];
     l:=csiz;
     repeat			{compiliert besser als FOR}
      with xp^ do case carg of
       ARG_I08: xx.x:=h;
       ARG_I16: xx.x:=i;
       ARG_I32,
       ARG_U32: xx.x:=l;
       ARG_U16: xx.x:=w;
       ARG_U08: xx.x:=b;
       ARG_F32: xx.x:=s;
       ARG_F64: xx.x:=x;
      end;
      rp^:=xx.x;
{$IFOPT D+}
      str(rp^:3:3,s);
      lstrcat(s,#10);
      mexPrintf0(s);
{$ENDIF}
      Inc(rp);
      Inc(PChar(xp),carg and $0F);
      Dec(l);
     until l=0;
    end;
   end{if};
  end{for};
 end;

function R2I(pm: PArray; min,max: Integer; var r: array of Integer):Boolean;
 var
  zp: PDouble;
  i,e: Integer;
 begin
  R2I:=false;
  if not mxIsNumeric(pm) then exit;
  zp:=mxGetPr(pm);
  e:=mxGetM(pm);
  if e>HIGH(r) then e:=HIGH(r);
  for i:=0 to e do begin
   if (zp^<min) or (zp^>max) then exit;
   r[i]:=Round(zp^);
   Inc(zp);
  end;
  R2I:=true;
 end;

procedure mexFunction(nlhs:Integer; var plhs:TMatArr;
  nrhs:Integer; var prhs:TMatArr); stdcall;
 var
  i: Integer;
  t: Byte;
  pm: ^PArray;
  x: Double;
{ intp: PInteger;
  s: array[0..255] of Char;}
 begin
  FillChar(allocs,sizeof(allocs),0);
  {if Catch(CatchBuf)=0 then} begin
   if nrhs>=1 then begin
    FillChar(argt,sizeof(argt),0);
    mxGetString(prhs[0],ParseStr,sizeof(ParseStr)-1);
    if nlhs<>0 then argt[0]:=ARG_I16;
    pm:=@prhs[0];
    for i:=1 to nrhs-1 do begin
     Inc(pm);
     if mxIsChar(pm^) then t:=ARG_Str or ARG_Ptr
     else t:=ARG_I16;
     sizes[i]:=mxGetMxN(pm^);
     if sizes[i]>1 then t:=t or ARG_Ptr;
     argt[i]:=t;
    end;
    ParseString;
{$IFOPT D+}
    ShowProto(nrhs-1);
{$ENDIF}
    x:=CallEntry(nrhs-1,prhs);
    if argt[0]<>0 then begin
     plhs[0]:=mxCreateDoubleMatrix(1,1,mxREAL);
     mxGetPr(plhs[0])^:=x;
    end;

   end else mexPrintf0(
     '1 und mehr Parameter:'#10+
     '1. ''[DLL-Name:]Eintrittspunkt[ Prototyp]'' (wird DLL-Name weggelassen, dann letztgenutzte)'#10+
     '   Funktionsprototyp ''i=i,u32,f,s,i16,i32'' i=int, u=unsigned, f=float, s=asciiz'#10+
     '2.. Parameter'#10+
     'Mehr Info: help calldll'#10);
  end;
  for i:=0 to 17 do if allocs[i]<>nil then LocalFree(Integer(allocs[i]));
 end;

exports
 mexFunction;

var
 OldExitProc: Pointer;

procedure NewExitProc;
 begin
  ExitProc:=OldExitProc;
  FreeLibrary(hDllLast);
 end;

begin
 DisableThreadLibraryCalls(HInstance);
 OldExitProc:=ExitProc;
 ExitProc:=@NewExitProc;
 hDllLast:=LoadLibrary('user32.dll');
end.
Detected encoding: OEM (CP437)1
Wrong umlauts? - Assume file is ANSI (CP1252) encoded