Quelltext /~heha/messtech/mio_dde.zip/mio_dde.pas

program mio_dde;
{$D AT-MIO-16X-zu-DDE-Schnittstelle h#s 04/01}
{Bentigt NIDAQ.DLL (16bit) zum Zugriff}
{$F-,A+,G+,K+,W-}
{$I-,Q-,R-,S-}
{$V+,B-,X+,T+,P+}
{$M 8192,0}
{$N+}
{$R mio_dde}
{$DEFINE MSGBOX}
{$DEFINE ERRMSG}

uses WinProcs,WinTypes,Win31,Ddeml, WDAQ,WUtils,Parser,Tray16;

const
 AppName='MIO_DDE';
 AppTitle='DDE<->MIO';
 HelpFileName='MIO_DDE.HLP';	{wenn berhaupt}
 BOARD=1;		{hier: festgelegt}
{************* Unterprogramme *************************}
type
 PConstItem=^TConstItem;
 TConstItem=record
  k: Integer;
  s: array[0..7] of Char;
 end;

 TReal=Double;
 PDouble=^Double;

function Real2S(Z:TReal; F:Integer; S:PChar):PChar;
{Diese Funktion arbeitet wie Str(Z:F:F,S), jedoch werden unntige
 Nachkomma-Nullen abgeschnitten.
 Als Ergebnis wird der Stringzeiger durchgereicht.
 Der Str()-Syntaxcheck ist buggy; daher mu mit einem wilden Typecast
 der Compiler ruhiggestellt werden}
 begin
  Real2S:=S;		{Stringzeiger durchreichen}
  Str(Z:F:F,TS255(Pointer(S)^));	{mittels Systemfunktion wandeln}
  S:=lStrChr(S,'.');	{Dezimalpunkt enthalten?}
  if S<>nil then begin
   Inc(S,lstrlen(S));	{Auf die Null}
   repeat
    Dec(S);		{Zeiger aufs Stringende (vor die Null)}
    if S^='.' then begin {String besteht nur (noch) aus dem Dezimalpunkt?}
     S^:=#0;		{String krzen und raus!}
     break;
    end;
    if S^='0' then S^:=#0	{Stringende ist die Null? - Krzen und weiter}
    else break;		{sonst raus!}
   until false;
  end;
 end;

function S2Real(S:PChar; var Z: TReal):boolean;
{wie Val(), jedoch vorher Komma zu Punkt wandeln.
 Das Ergebnis ist TRUE, wenn die Konversion OK verlief; dann und nur dann
 wird auch die Variable Z eingeschrieben.
 Weiraum (TAB & SPC) am Anfang wird bergangen, Weiraum (#0..' ')
 am Ende auch, beim Abhacken wird das Zeichen zwischendurch gemerkt,
 d.h. der String S wird nicht nach auen verndert.
 Da die dusselige Pascal-Val()-Funktion bei Fehler 0.0 einschreibt,
 geht's nicht ohne Umweg ber eine lokale Real-Variable.
 Ein Jammer, da weder Windows noch Pascal vernnftiges Handling mit
 Real-Zahlen anbieten}
 var
  I:Integer;
  SP: PChar;
  MemChr: Char;
  ZT: TReal;
 begin
  while (S^=' ') or (S^=#9) do Inc(S);	{Weiraum am Anfang bergehen}
  SP:=lStrChr(S,',');
  if SP<>nil then SP^:='.';	{Komma zum Punkt machen}
  SP:=S;
  while SP^>' ' do Inc(SP);	{Ende des Strings suchen}
  MemChr:=SP^; SP^:=#0;		{Zeichen merken, String abhacken}
  Val(S,ZT,I);
  SP^:=MemChr;			{Zeichen zurckschreiben}
  if I=0 then Z:=ZT;		{Nur bei fehlerfreier Konversion rckschreiben}
  S2Real:= (I=0);		{false wenn Fehler in Real-Zahl}
 end;

function GetIntArg2(Str:PChar; min,max: Integer;
  const ConstList: array of TConstItem; var res:Integer):Boolean;
{mit Auswertung einer Konstantenliste}
 label
  found;
 var
  x: Double;
  sp: PChar;
  argc,e,i,j,y: Integer;	{argc=Anzahl Konstanten in Liste}
  argv: array[0..15] of PChar;	{Tokenisierte Konstantenliste}
 begin
  GetIntArg2:=false;
  argc:=0;
  FillChar(argv,sizeof(argv),0);
  sp:=Str;
  repeat
   case sp^ of
    #0,' ','|','+': begin		{nur ODER erlaubt}
     if argv[argc]<>nil then begin
      if argc=16 then break;		{keine weiteren Argumente!}
      inc(argc);			{Nchste Konstante erwarten}
     end;
     if sp^=#0 then break
     else sp^:=#0;			{Weiraum lschen}
    end;
    else if argv[argc]=nil then argv[argc]:=sp;
   end;
   Inc(sp);
  until false;
  if argc=0 then exit;		{Fehler: keine Konstante angegeben}
  y:=0;
  for j:=argc-1 downto 0 do begin
   for i:=HIGH(ConstList) downto 0 do
   with ConstList[i] do begin
    if lstrcmpi(argv[j],s)=0 then begin
     y:=y or k;
     goto found;
    end;
   end;
   Val(argv[j],i,e);		{Vielleicht ist's ja eine gewhnliche Zahl?}
   if e=0 then y:=y or i
   else exit;			{Fehler: Konstante nicht gefunden}
found:
  end;
  res:=y;
  GetIntArg2:=true;
 end;

{************* Hauptprogramm *************************}
const
 ARG_U08	=$01;
 ARG_U16	=$02;
 ARG_U32	=$04;
 ARG_vzb	=$80;		{bei vzb. Zahl}
 ARG_bigend	=$20;		{Motorola statt Intel}
type
 PShortInt=^ShortInt;

var
 DdeHelpString: array[0..1024] of Char;		{zum Zusammensetzen}
type
 eTopic=(SYS,ADC,DAC);
 eSysItem=(SYST,TOPICS,FORMATS,HELP,TIL,SYSITEMS);
 eItem=(DATA,LIMITS,MODE,WAVE,RATE,BLOCKLEN,REPEATS,CONTROL,VERSION);

const
 DdeSystemS: array[eSysItem]of PChar=(
   'System','Topics','Formats','Help','TopicItemList','SysItems');
	{System-Item-Stringkonstanten}

 DdeStrings:array[eSysItem]of PChar=(
  nil,
  'ADC DAC',
  'CF_TEXT CF_XlTable CF_MatTable',
  DdeHelpString,
  'data limits mode wave rate blocklen repeats control version',
  'Topics Formats Help TopicItemList SysItems');
	{Stringkonstanten als Antwort auf Anfragen auf System-Items}

 DataS: array[eItem] of PChar=(
  'data',
  'limits',
  'mode',
  'wave',
  'rate',
  'blocklen',
  'repeats',
  'control',
  'version');

 DdeService='MIO';
const
 DdeTopicS: array[etopic] of PChar=(
  'SYSTEM',
  'ADC',
  'DAC');
var
 Inst: LongInt;				{Instanz-Variable ("Handle"), GLOBAL}
 ServiceHsz: Hsz;			{Stringhandle fr Service (GPIB)}
 TopicHsz: array[eTopic] of Hsz;	{"System", "ADC", "DAC"}
 SysHsz: array[eSysItem] of Hsz;	{System-Stringhandles}
 DdeConnections: Integer;		{Anzahl aktiver Verbindungen}
 CF_XlTable: Word;			{Handle fr Binrdaten}
 CF_MatTable: Word;

const
 tdtTable	=$0010;	{atype}
 tdtFloat	=$0001;	{btype}
type
 PExcelG=^TExcelG;	{generischer Excel-Header}
 TExcelG=record
  gType, gSize: Word;
 end;
 PExcelA=^TExcelA;	{Array-Header}
 TExcelA=record
  g: TExcelG;		{zuerst generischer Header}
  Rows, Cols: Word;	{dann Dimensionen}
 end;
 PExcelHeader=^TExcelHeader;
 TExcelHeader=record
  a: TExcelA;		{Header fr Array-Dimensionen}
  b: TExcelG;		{Header fr Doubles}
 end;

 PMatHeader=^TMatHeader;
 TMatHeader=record
  datatype: LongInt;	{6=Double}
  dimensions: LongInt;	{Dimensionen, 0=Skalar}
  dims: array[0..1] of LongInt;
 end;

function DecodeDataType(SH:HSz):Byte;
{liefert 0 wenn String-Handle undekodierbar}
(* Aufbau des Formatbezeichners: [u|i]{8|16|32}[i|m] *)
{Standard ist "u" (fr unsigned) und "i" (fr Intel)}
 var
  sp: PChar;
  dt: Byte;
  s: TS31;
 begin
  dt:=0;
  DecodeDataType:=0;
  DdeQueryString(Inst,SH,s,sizeof(s),CP_WinAnsi);
  sp:=AnsiUpper(s);
  case sp^ of
   'U': Inc(sp);		{Standard}
   'I': begin dt:=dt or ARG_vzb; Inc(sp); end;
  end;
  case sp^ of
   '8': begin dt:=dt or ARG_U08; Inc(sp); end;
   '1': if sp[1]='6' then begin dt:=dt or ARG_U16; Inc(sp,2); end;
   '3': if sp[1]='2' then begin dt:=dt or ARG_U32; Inc(sp,2); end;
   else exit;			{ist Pflichtparameter}
  end;
  case sp^ of
   'I': Inc(sp);		{Standard}
   'M': begin dt:=dt or ARG_bigend; Inc(sp); end;
  end;
  if sp^<>#0 then exit;		{Fehler, wenn String hier nicht zu Ende!}
  DecodeDataType:=dt;
 end;

function MakeXlBuffer(k:Word):PDouble;
{Erzeugt einen Puffer frs Excel-Clipboardformat, gefllt mit dem Header,
 Rckgabewert zeigt dahinter, Freigabe mit GlobalFree(PtrRec(k).Sel)}
 var
  ehp: PExcelHeader;
  zh: Integer;	{Anzahl der Zwischen-Header, immer 2 pro 32k?}
 begin
  zh:=k div $1FFF;		{also max. $1FFF Doubles pro Block = 64K-8}
  ehp:=GlobalAllocPtr(GMEM_Fixed,
    sizeof(TExcelA)+LongMul(k,sizeof(Double)+zh*sizeof(TExcelG)));
  if ehp<>nil then begin
   with ehp^ do begin
    a.g.gType:=tdtTable;
    a.g.gSize:=4;
    a.Rows:=1;
    a.Cols:=k;
    b.gType:=tdtFloat;
    b.gSize:=minW(k,$1FFF)*8;
   end;
   Inc(ehp);
  end;
  MakeXlBuffer:=PDouble(ehp);
 end;

function MakeMatBuffer(k:LongInt):PDouble;
 var
  mhp: PMatHeader;
  dims: Integer;
 begin
  dims:=0; if k<>1 then Inc(dims);	{null- oder eindimensional}
  mhp:=GlobalAllocPtr(GMEM_Fixed,
    LongMul(2+dims,sizeof(LongInt))+k*sizeof(Double));
  if mhp<>nil then begin
   mhp^.datatype:=6;
   mhp^.dimensions:=dims;
   if dims<>0 then mhp^.dims[0]:=k;
   Inc(mhp);
   if dims=0 then Dec(PLongInt(mhp));
  end;
  MakeMatBuffer:=PDouble(mhp);
 end;

type
 THugeDdeData=record
  h: hDdeData;		{fr DdeCreateDataHandle}
  l: LongInt;		{Offset fr DdeAddData, gleichzeitig Fllstand}
 end;

procedure hddeput(var hd:THugeDdeData; dp:PChar; ds:Word);
{FAR-Daten an HUGE-DDE-Puffer anhngen}
 begin
  hd.h:=DdeAddData(hd.h,dp,ds,hd.l);
  Inc(hd.l,ds);
 end;


function Double2Dde(dp:PDouble;dlen:LongInt;SH:HSz;cf:Word):HDdeData;
{Wandelt Double-Array in DDE-Daten um}
 label
  except,except2;
 var
  readbuf,writebuf,ArgStr: PChar;
  writebd: PDouble absolute writebuf;
  havehash:Boolean;
  z1,z2,e: Integer;
  buf: TS15;		{Mini-Puffer}
  k: Word;
  L: LongInt;
  outdata: THugeDdeData;
 begin
  Double2Dde:=0;
  outdata.h:=DdeCreateDataHandle(Inst,nil,256,0,SH,cf,0);
  if outdata.h=0 then exit;	{ENoMem}
  outdata.l:=0;
  repeat
   ArgStr:=readbuf;
   begin
    k:=dlen;
    if cf=CF_XlTable then begin
     writebd:=MakeXlBuffer(k);
    end else if cf=CF_MatTable then begin
     writebd:=MakeMatBuffer(k);
    end else begin
     writebuf:=GlobalAllocPtr(GMEM_Fixed,dlen*24);
	{max. 5 Bytes pro Byte: "-128 "}
    end;
    if writebuf=nil then goto except;	{ENOMEM}

    while k>0 do begin
     if cf<>CF_Text then begin
      writebd^:=L;
      Inc(writebd);
     end else begin
      Str(L,TS31(Pointer(writebuf)^));
      if z2>1 then lstrcat(writebuf,' ');	{oder Tabulator->Zeilenvektor}
{Leider ist Matlab zu doof, daraus einen Zeilenvektor zu machen, so bleibt
 nur der Umweg ber str2num. Ich bin an dieser Stelle nicht gewillt,
 fr Matlab den Trenner 0D0A einzubauen; da wird ein Spaltenvektor draus...}
      Inc(writebuf,lstrlen(writebuf));
     end;
     dec(z2);
     dec(k);
    end;	{k Daten umgewandelt}
    hddeput(outdata,Ptr(PtrRec(writebuf).sel,0),PtrRec(writebuf).ofs);
    GlobalFreePtr(writebuf);
   end;
  until z2=0;
{bei TEXT bis zum ENDE-Signal lesen, bei Binrdaten bis zum Ende der Daten}

  GlobalFreePtr(readbuf);
  if cf=CF_Text then hddeput(outdata,'',1);
	{im Fall von Text eine abschlieende Null dazu}
  Double2Dde:=outdata.h;
  exit;
except:
  GlobalFreePtr(readbuf);
except2:
  DdeFreeDataHandle(outdata.h);
 end;

function Long2Dde(l:LongInt;SH:HSz;cf:Word):HDdeData;
{wandelt eine einzelne Zahl ins DDE-Format (Excel oder String) um}
 var
  S: TS31;
  pd: PDouble;
 begin
  if cf=CF_Text then begin
   Str(L,S);
   Long2Dde:=DdeCreateDataHandle(Inst,@s,lstrlen(s)+1,0,SH,cf,0);
  end else begin
   pd:=MakeXlBuffer(1);
   pd^:=l;
   PtrRec(pd).ofs:=0;
   Long2Dde:=DdeCreateDataHandle(Inst,pd,12+8,0,SH,cf,0);
   GlobalFreePtr(pd);
  end;
 end;

function Real2Dde(z:TReal;SH:HSz;cf:Word):HDdeData;
{wandelt eine einzelne Zahl ins DDE-Format (Excel oder String) um}
 var
  S: TS31;
  pd: PDouble;
 begin
  if cf=CF_Text then begin
   Str(z,S);
   Real2Dde:=DdeCreateDataHandle(Inst,@s,lstrlen(s)+1,0,SH,cf,0);
  end else begin
   pd:=MakeXlBuffer(1);
   pd^:=z;
   PtrRec(pd).ofs:=0;
   Real2Dde:=DdeCreateDataHandle(Inst,pd,12+8,0,SH,cf,0);
   GlobalFreePtr(pd);
  end;
 end;

{hier: als Ersatz fr TMemoryStream}
type
 THugeData=record
  h: THandle;		{fr GlobalAlloc}
  p: PChar;		{huge Lese- oder Schreibzeiger}
  l: LongInt;		{Lesen: Noch-Lnge, Schreiben: Fllstand}
 end;

function hinitw(var hd:THugeData; inisize:LongInt):Boolean;
 begin
  hd.h:=GlobalAlloc(0,inisize);
  hd.p:=GlobalLock(hd.h);
  hd.l:=0;
  hinitw:=hd.h<>0;
 end;

procedure hdonew(var hd: THugeData);
 begin
  GlobalUnlock(hd.h);
  GlobalFree(hd.h);
 end;

function hput(var hd:THugeData; inputs:PChar; inputl:Word):Boolean;
{FAR-Daten an HUGE-Puffer anfgen; dieser vergrert sich ggf. selbst}
 var
  l: LongInt;
 begin
  hput:=false;
  l:=hd.l+inputl;
  if GlobalSize(hd.h)<l then begin
   GlobalUnlock(hd.h);
   hd.h:=GlobalReAlloc(hd.h,l+$8000,0);	{32K extra}
   if hd.h=0 then exit;			{ENOMEM}
   hd.p:=GlobalLock(hd.h);
   IncHPL(hd.p,hd.l);			{Zeiger neu setzen}
  end;
  hmemcpy(hd.p,inputs,inputl);
  IncHP(hd.p,inputl);
  hd.l:=l;
  hput:=true;
 end;

function hget(var hd:THugeData; outputs:PChar; outputl:Word):Word;
{FAR-Daten aus HUGE-Puffer holen, bei outputl=0 Suche nach Weiraum
 PA: wirklich gelieferte Bytes; nullterminiert bei outputl=0}
 procedure MoveChar;
  begin
   outputs^:=hd.p^;
   Inc(outputs);
   Inc(outputl);
   IncHP(hd.p,1);
   Dec(hd.l);
  end;
 begin
  if outputl<>0 then begin
   if outputl>hd.l then outputl:=LongRec(hd.l).lo;
   if outputs<>nil then hmemcpy(outputs,hd.p,outputl);
   IncHP(hd.p,outputl);
   Dec(hd.l,outputl);
  end else begin
   while (hd.l>0) and (hd.p^<=' ') do MoveChar;
   while (hd.l>0) and (hd.p^>' ')  do MoveChar;
   outputs^:=#0;	{Terminierung setzen}
  end;
  hget:=outputl;
 end;

function putDouble(var hd:THugeData; d:Double):Boolean;
{Steckt Double MemoryStream.
 Aufgrund der glcklichen Lage, einen Intel-Prozessor zu haben,
 macht die Daten-Gre hier kaum Probleme}
 begin
  putDouble:=hput(hd,PChar(@d),8);
 end;

function Dde2Double(var dp:PDouble; var x,y:LongInt;data:HDdeData;cf:Word):Boolean;
{holt Daten aus <data>, interpretiert sie ggf. binr je nach cf
 und macht daraus ein bis zu zweidimensionales Double-Array}
{Hier wird mit huge-Pointern gearbeitet! (igitt!)}
 label except;
 var
  indata,outdata:THugeData;
  eha: TExcelA;
  ehg: TExcelG absolute eha;
  Z: Double;
  L: LongInt;
  ec: Integer;
  cw: Word;
  s2: PChar;
  ss: TS31;
{$IFDEF MSGBOX}
  vsrec: record
   x,y:LongInt;
  end;
  SM: TS255;
{$ENDIF}
 begin
  Dde2Double:=false;
  x:=0; y:=1;
  asm int 3 end;
  indata.p:=DdeAccessData(data,@indata.l);
  if hinitw(outdata,indata.l)=false	{ungefhr gleiche Lnge}
  then goto except;

  if cf=CF_XlTable then begin
   L:=0;
   while hget(indata,PChar(@ehg),4)=4 do begin
    if ehg.gType=tdtTable then begin
     if ehg.gSize<>4 then break;	{Gren-Fehler}
     if hget(indata,PChar(@eha.rows),4)<>4 then break;
     Inc(L,LongMul(eha.rows,eha.cols));	{was an Doubles so folgt...}
    end else if ehg.gType=tdtFloat then begin;
     cw:=ehg.gsize;
     if L<cw then break;
     Dec(L,cw);				{was an Doubles spter kommt...}
     while (cw>0) and (hget(indata,PChar(@Z),8)=8) do begin
      if not hput(outdata,PChar(@Z),8) then goto except;
      Inc(x);
      Dec(cw);
     end;
    end else begin	{unbekannter EXCEL-Binrtyp: bergehen!}
     if hget(indata,nil,ehg.gsize)<>ehg.gsize then break;
    end;
   end;

  end else if cf=CF_MatTable then begin
   x:=1;
   if hget(indata,PChar(@L),4)<>4 then goto except;
   if L<>6 then goto except;		{Falsches Kennbyte}
   if hget(indata,PChar(@L),4)<>4 then goto except;
   if L>2 then goto except;		{Zu viele Dimensionen}
   if L<>0 then begin
    if hget(indata,PChar(@x),4)<>4 then goto except;
    if L>1 then begin
     if hget(indata,PChar(@y),4)<>4 then goto except;
    end;
   end;
   L:=x*y;
   while L<>0 do begin
    if hget(indata,PChar(@Z),8)<>8 then goto except;
    if not hput(outdata,PChar(@Z),8) then goto except;
    Dec(L);
   end;

  end else if cf=CF_Text then begin
   while hget(indata,ss,0)>0 do begin
    if S2Real(ss,Z) and (not PutDouble(outdata,Z)) then goto except;
    Inc(x);
   end;
  end;

  GlobalUnlock(outdata.h);
  outdata.p:=GlobalLock(outdata.h);	{Zeiger zurckstellen}
  dp:=PDouble(outdata.p);
{  hdonew(outdata);}
  DdeUnaccessData(data);
  Dde2Double:=true;	{OK}
{$IFDEF MSGBOX}
  vsrec.x:=x;
  vsrec.y:=y;
  wvsprintf(SM,'Erkannte Array-Dimensionen: x=%ld, y=%ld',vsrec);
  MessageBox(0,SM,'MIO_DDE Dde2Double',0);
{$ENDIF}
  exit;
except:
  DdeUnaccessData(data);
 end;

function Dde2Real(var Z:TReal;data:HDdeData;cf:Word):Boolean;
{holt Daten aus <data>, interpretiert sie ggf. binr je nach cf
 und macht daraus Z}
 label finally;
 type
  POneReal=^TOneReal;
  TOneReal=record
   eh: TExcelHeader;
   zz: Double;
  end;
 var
  indata: PChar;
 begin
  Dde2Real:=false;
  indata:=DdeAccessData(data,nil);
  if cf<>CF_Text then with POneReal(indata)^ do begin
   if eh.a.g.gType<>tdtTable then goto finally;
   if eh.a.g.gSize<>4 then goto finally;
   if eh.a.rows=0 then goto finally;
   if eh.a.cols=0 then goto finally;
   if eh.b.gType<>tdtFloat then goto finally;
   if eh.b.gSize<8 then goto finally;
   Z:=zz;
   Dde2Real:=true;
  end else begin {CF_Text}
   Dde2Real:=S2Real(indata,Z);
  end;
finally:
  DdeUnaccessData(data);
 end;

var
 MainWnd: HWnd;

procedure ChangeDdeDisplay;
 var
  s: TS31;
  vsrec:record
   s: PChar;
   i: Integer;
  end;
 begin
  vsrec.s:=AppTitle;
  vsrec.i:=DdeConnections;
  wvsprintf(s,'%s [%d]',vsrec);
  SetWindowText(MainWnd,s);
 end;

procedure DdeError(Code:Integer);
 begin
  MBox(MainWnd,102,Code);
 end;

function CreateStringHandle(Str:PChar):Hsz;
 var Stringhandle: Hsz;
 begin
  Stringhandle:=DdeCreateStringHandle(Inst,Str,CP_WinAnsi);
  CreateStringHandle:=StringHandle;
  if StringHandle=0 then DdeError(2);
 end;

procedure FreeStringHandle(Stringhandle:Hsz);
 begin
  if Stringhandle<>0 then
   DdeFreeStringHandle(Inst,StringHandle);
 end;

function atoi(var S:PChar):Integer;
{Numerisch wandeln, String-Zeiger vorrcken}
 var
  i,ec,ec2:Integer;
  ch:Char;
 begin
  Val(S,i,ec);
  if ec<>0 then begin
   if ec>1 then begin
    Dec(ec);
    ch:=S[ec];
    S[ec]:=#0;		{Patch}
    Val(S,i,ec2);	{i sollte nun stimmen}
    S[ec]:=ch;		{Rckpatch}
    Inc(S,ec);		{S vorrcken aufs "falsche" Zeichen}
   end;
  end else Inc(S,lstrlen(S));	{S vorrcken aufs String-Ende}
  atoi:=i;
 end;

function GetDataNumber(Hsz2: Hsz; Fmt:Word; Chan: array of ShortInt;
  var ChanHigh:Integer):eItem;
 label raus,fail;
 var
  I: eItem;
  from_ch,to_ch:Integer;
  sp: PChar;
  ch_hi: Integer;
  S:TS31;
{$IFDEF MSGBOX}
  S2: TS255;
{$ENDIF}
 begin
  if (Fmt=CF_Text) or (Fmt=CF_XlTable) or (Fmt=CF_MatTable) then begin
{wenn passendes HSZ1(Topic) und passendes Format(Text), dann weiter}
   ch_hi:=-1;
   from_ch:=0;
   DdeQueryString(Inst,Hsz2,S,sizeof(s),CP_WinAnsi);
   for i:=LOW(eItem) to HIGH(eItem) do begin
    if lstrcmp1(dataS[i],S,lstrcmpi)=0 then begin
     sp:=S+lstrlen(dataS[i]);	{wo die Zahlen losgehen knnen}
     while sp^<>#0 do begin
      while sp^ in [#9,' ',',',';'] do Inc(sp);	{Weiraum und Trennzeichen bergehen}
      if sp^='-' then begin
       Inc(sp);
       to_ch:=atoi(sp);
       if Word(to_ch)>HIGH(chan) then goto fail;
       while from_ch<=to_ch do begin
	if ch_hi>=HIGH(chan) then goto fail;
	Inc(ch_hi);
	Chan[ch_hi]:=from_ch;
	ChanHigh:=ch_hi;	{ohne Zahlen bleibt's unverndert!}
	Inc(from_ch);
       end;
      end else begin
       if ch_hi>=HIGH(chan) then goto fail;
       from_ch:=atoi(sp);
       if Word(from_ch)>HIGH(chan) then goto fail;
       if ch_hi>=HIGH(chan) then goto fail;
       Inc(ch_hi);
       Chan[ch_hi]:=from_ch;
       ChanHigh:=ch_hi;		{ohne Zahlen bleibt's unverndert!}
       Inc(from_ch);
      end;
     end;
     if ChanHigh<0 then goto fail;	{ungesetzt? Fehler!}
     goto raus;
    end;
   end;
  end;
fail:
  ShortInt(i):=-1;
raus:
  GetDataNumber:=i;
{$IFDEF MSGBOX}
  if i in [LOW(eItem)..HIGH(eItem)] then begin
   sp:=s2;
   Inc(SP,wvsprintf(SP,'letzter Kanal: %d'#10,ChanHigh));
   for ch_hi:=0 to ChanHigh do begin
    Inc(SP,wvsprintf(SP,'%d ',Chan[ch_hi]));
   end;
   MessageBox(0,s2,'MIO_DDE GetDataNumber',0);
  end;
{$ENDIF}
 end;

function GetSystemNumber(Hsz2: Hsz; Fmt:Word):eSysItem;
 label raus;
 var
  I: eSysItem;
 begin
  if Fmt<>CF_Text then exit;
  for I:=LOW(eSysItem) to HIGH(eSysItem) do
  if DdeCmpStringHandles(Hsz2,SysHsz[I])=0 then goto raus;
  ShortInt(I):=-1;
raus:
  GetSystemNumber:=I;
 end;

function GetUD(hszTopic:HSZ):eTopic;
 label raus;
 var
  I: eTopic;
 begin
  for I:=LOW(TopicHsz) to HIGH(TopicHsz) do
  if TopicHsz[i]=hszTopic then goto raus;
  ShortInt(I):=-1;
raus:
  GetUD:=I;
 end;

function Get_Limits(xp:PDouble; l:LongInt; var xa,xe:Double):Boolean;
 begin
  xa:=100; xe:=-100;
  while l<>0 do begin
   if xa>xp^ then xa:=xp^;	{Minimum suchen}
   if xe<xp^ then xe:=xp^;	{Maximum suchen}
   IncHP(PChar(xp),sizeof(Double));
   Dec(l);
  end;
  Get_Limits:=(-10<=xa) and (xe<=10);
{$IFDEF ERRMSG}
  if (xa<10) or (xe>10) then begin
   MessageBox(0,'Bereich zu gro!','MIO_DDE Get_Limits',0);
  end;
{$ENDIF}
 end;

function Get_Polarity(xp:PDouble; l:LongInt; var pol:Boolean):Boolean;
 var
  xa,xe:Double;
 begin
  Get_Polarity:=Get_Limits(xp,l,xa,xe);
  pol:=xa<0;		{BIpolar wenn Minimum negativ}
 end;

function Get_Gain(xp:PDouble; l:LongInt; var gain:Byte; var pol:Boolean):Boolean;
 const
  gainEnd: array[0..6] of Single=(10,5,2,1,0.5,0.2,0.1);
  gainVal: array[0..6] of Byte=(1,2,5,10,20,50,100);
 var
  xa,xe:Double;
  im: Integer;
 begin
  Get_Gain:=Get_Limits(xp,l,xa,xe);
  pol:=xa<0;
  xa:=abs(xa); xe:=abs(xe);
  if xe<xa then xe:=xa;		{vzl. Maximum in XE ermitteln}
  for im:=0 to 6 do begin
   if xe>gainEnd[im] then break;
   gain:=gainVal[im];
  end;
 end;

function HandleError(ec:Integer):Boolean;
 begin	{Wohin soll blo das Output gehen??}
  HandleError:=false;
 end;

var
 adc_channels:	array[0..15] of ShortInt;	{enthlt aktuelle Zuordnung!}
 adc_channel_high: Integer;
 adc_bipolar:	array[0..15] of Boolean;	{hier statische Zuordnung!}
 adc_gain:	array[0..15] of Byte;
 dac_channels:	array[0..1] of ShortInt;
 dac_channel_high: Integer;
 dac_bipolar:	array[0..1] of Boolean;

function DdeCallback(CallType,Fmt:Word; Conv:hConv; HSz1,HSz2:HSz;
  aData:hDdeData; Data1,Data2: LongInt): hDdeData; export;
 const
  ConnectingUD:Integer=0;	{zum Hinberretten nach XTYP_Connect_Confirm}
 var
  SysItemNum: eSysItem;
  ItemNum: eItem;
  i,k: Integer;
  W: Word;
  Z: TReal;
  S: TS31;
  sp: PChar;
  ok: Boolean;
  ud: eTopic;
  ec: Integer;
  bdidx,pad,sad,tmo,eot,eos:Integer;
  by: Byte;
  pd: PDouble;
  x,y: LongInt;
  um: Integer;
 begin
  DdeCallback:=0;
  case CallType of
   XTYP_Connect: begin
    case GetUD(hsz1) of
     LOW(eTopic)..HIGH(eTopic): begin
      Inc(DdeConnections);
      ChangeDdeDisplay;
      DdeCallback:=1;		{Verbindung okay!}
     end;
    end;
   end;

   XTYP_Disconnect: begin
    Dec(DdeConnections);
    ChangeDdeDisplay;
   end;

   XTYP_Request: begin
    ud:=GetUD(hsz1);
    case ud of
     SYS: begin
      SysItemNum:=GetSystemNumber(hsz2,fmt);
      case SysItemNum of
       LOW(eSysItem)..HIGH(eSysItem): begin
	sp:=DdeStrings[SysItemNum];
	DdeCallback:=DdeCreateDataHandle(Inst,sp,
	 lstrlen(sp)+1,0,hsz2,CF_Text,0);
       end;
      end;
     end;
     ADC: begin
      ItemNum:=GetDataNumber(hsz2,fmt,adc_channels,adc_channel_high);
      case ItemNum of
       DATA: begin
	by:=DecodeDataType(hsz2);
       end;
       LIMITS:;
       MODE:;
       WAVE:;
       RATE:;
       BLOCKLEN:;
       REPEATS:;		{keine Wiederholungen mglich!}
       CONTROL:;
       VERSION: begin
	DdeCallback:=DdeCreateDataHandle(Inst,
	  PChar('1.0, h#s 05/01, 16bit National Instruments'),
	  43,0,hsz2,CF_Text,0);
       end;
      end{case ItemNum};
     end;
     DAC: begin
      ItemNum:=GetDataNumber(hsz2,fmt,dac_channels,dac_channel_high);
      case ItemNum of
       DATA:;
       LIMITS:;
       MODE:;
       WAVE:;			{kein Wave-Input}
       RATE:;
       BLOCKLEN:;
       REPEATS:;		{keine Wiederholungen mglich!}
       CONTROL:;
      end;
     end;
    end;
   end;

   XTYP_Poke: begin
    DdeCallback:=DDE_FNotProcessed;	{Pessimistisch}
    ud:=GetUD(hsz1);
    case ud of
     SYS:;
     ADC: begin
      ItemNum:=GetDataNumber(hsz2,fmt,adc_channels,adc_channel_high);
      Dde2Double(pd,x,y,aData,fmt);
      if y=adc_channel_high+1 then case ItemNum of
       LIMITS: begin
	OK:=true;
	for i:=0 to adc_channel_high do begin
	 k:=adc_channels[i];
	 OK:=Get_Gain(pd,x,adc_gain[k],adc_bipolar[k]) and OK;
	end;
	if OK then DdeCallback:=DDE_FAck;
       end;
       MODE: if x=1 then begin
	z:=pd^;
	if (0<=z) and (z<=2) then begin
	 for i:=0 to adc_channel_high do begin
	  k:=adc_channels[i];
	  AI_Configure(BOARD,k,Round(z),0,not adc_bipolar[k],0);
	 end;
	 DdeCallback:=DDE_FAck;
	end;
       end;
       RATE:;
       BLOCKLEN:;
       CONTROL:;
      end{case};
      GlobalFreePtr(pd);
     end;
     DAC: begin
      ItemNum:=GetDataNumber(hsz2,fmt,dac_channels,dac_channel_high);
      Dde2Double(pd,x,y,aData,fmt);
      if y=dac_channel_high+1 then case ItemNum of
       DATA: if x=1 then begin
	for i:=0 to dac_channel_high do begin
	 k:=dac_channels[i];
	 ec:=AO_VWrite(BOARD,k,pd^);
	 if ec=-71 then begin	{outOfRangeErr}
	  if pd^>5
	  then if dac_bipolar[k] then um:= 32767 else um:=Integer(65535)
	  else if dac_bipolar[k] then um:=-32768 else um:=0;
	  ec:=AO_Write(1,k,um);
	 end;
	 IncHP(PChar(pd),8);
	end;
	if dac_channel_high<>0 then AO_Update(1);	{in einem Schlag!}
	if HandleError(ec) then	DdeCallback:=DDE_FAck;
       end;
       LIMITS: begin
	OK:=true;
	for i:=0 to dac_channel_high do begin
	 k:=dac_channels[i];
	 OK:=Get_Polarity(pd,x,dac_bipolar[k]) and OK;
	end;
	if OK then DdeCallback:=DDE_FAck;
       end;
       MODE: if (x=1) or (x=2) then begin
	z:=pd^; IncHP(PChar(pd),8);
	if (0<=z) and (z<=2) then begin
	 for i:=0 to dac_channel_high do begin
	  k:=dac_channels[i];
	  if x=1
	  then AO_Configure(BOARD,k,not dac_bipolar[k],0,10 ,Round(z))
	  else AO_Configure(BOARD,k,not dac_bipolar[k],1,pd^,Round(z));
	 end;
	 DdeCallback:=DDE_FAck;
	end;
       end;
       WAVE: begin
       end;
       RATE:;
       REPEATS:;
       CONTROL:;
      end;
      GlobalFreePtr(pd);
     end;
    end;
   end;

  end;
 end;

procedure DDEInit;
 var
  W: Word;
  I: eTopic;
  J: eSysItem;
 begin
  if DdeInitialize(Inst,DdeCallBack,CBF_Skip_Registrations
    or CBF_Skip_Unregistrations
    or CBF_Fail_Advises,0)<>DMLErr_No_Error
  then DdeError(DMLErr_Sys_Error);
  W:=LoadString(Seg(HInstance),106,DdeHelpString,256);
  Inc(W,LoadString(Seg(HInstance),107,DdeHelpString+W,256));
  LoadString(Seg(HInstance),108,DdeHelpString+W,256);
  ServiceHsz:=CreateStringHandle(DdeService);
  for I:=LOW(eTopic) to HIGH(eTopic)
  do TopicHsz[I]:=CreateStringHandle(DdeTopicS[I]);
  for J:=LOW(eSysItem) to HIGH(eSysItem)
  do SysHsz[J]:=CreateStringHandle(DdeSystemS[J]);
  OemToAnsi(DdeStrings[HELP],DdeStrings[HELP]);
  DdeConnections:=0;
  if DdeNameService(Inst,ServiceHsz,0,DNS_Register)=0
  then DdeError(3);
 end;

procedure DdeDone;
 var
  I: eTopic;
  J: eSysItem;
 begin
  DdeNameService(Inst,ServiceHsz,0,DNS_Unregister);
  for J:=LOW(eSysItem) to HIGH(eSysItem)
  do FreeStringHandle(SysHsz[J]);
  for I:=LOW(eTopic) to HIGH(eTopic)
  do FreeStringHandle(TopicHsz[i]);
  FreeStringHandle(ServiceHsz);
  DdeUninitialize(Inst);
 end;

var
 traydata: TNotifyIconData;

function MainWndProc(Wnd:HWnd; Msg,wParam:Word; lParam:LongInt):LongInt; export;
 var
  lPar: LongRec absolute lParam;
  P: TPoint;
  m,sysm: HMenu;
  calldef: Boolean;
  s: TS31;
 begin
  calldef:=false;
  MainWndProc:=0;
  case Msg of
   WM_Create: begin
    sysm:=GetSystemMenu(Wnd,false);
    DeleteMenu(sysm,SC_Maximize,0);
    DeleteMenu(sysm,SC_Restore,0);
    DeleteMenu(sysm,SC_Size,0);
    DeleteMenu(sysm,SC_Minimize,0);
    LoadString(Seg(HInstance),101,s,sizeof(s));	{ber...}
    InsertMenu(sysm,0,MF_ByPosition or MF_String,$1F0,s);
    LoadString(Seg(HInstance),111,s,sizeof(s));	{Hilfe}
    InsertMenu(sysm,1,MF_ByPosition or MF_String,$1D0,s);
    if Swap(Word(GetVersion))>=3*256+95 then begin
     LoadString(Seg(HInstance),110,s,sizeof(s));	{In den Tray!}
     InsertMenu(sysm,2,MF_ByPosition or MF_String,$1E0,s);
     if (CmdShow=SW_Minimize) or (CmdShow=SW_ShowMinimized)
     or (CmdShow=SW_ShowMinNoActive)
     then PostMessage(Wnd,WM_SysCommand,$1E0,0);
    end;
   end;
   WM_SetText: begin
    if LongRec(traydata.cbSize).lo<>0 then begin
     lstrcpy(traydata.szTip,PChar(lParam));
     LongRec(traydata.uFlags).lo:=NIF_Tip;		{nur Text ndern}
     Shell_NotifyIcon(NIM_Notify,traydata);
    end;
    calldef:=true;
   end;
   WM_SysCommand: case wParam and $FFF0 of
    $1F0: MBox1(Wnd,100{about},nil);
    $1E0: begin
     LongRec(traydata.cbSize).lo:=sizeof(traydata);
     LongRec(traydata.hwnd).lo:=Wnd;
     LongRec(traydata.uID).lo:=110;
     LongRec(traydata.uFlags).lo:=NIF_Icon or NIF_Tip or NIF_Message;
     LongRec(traydata.uCallbackMessage).lo:=WM_User;
     LongRec(traydata.hIcon).lo:=GetClassWord(Wnd,GCW_HIcon);
     GetWindowText(Wnd,traydata.szTip,sizeof(traydata.szTip));
     Shell_NotifyIcon(NIM_Add,traydata);
     ShowWindow(Wnd,SW_Hide);
    end;
    $1D0: WinHelp(Wnd,HelpFileName,HELP_Index,0);
    else calldef:=true;
   end;
   WM_QueryOpen: ;			{nur Icon}
   WM_User: case lParam of
    WM_LButtonDown: begin
     {ShowWindow(Wnd,SW_ShowMinimized);}
     MBox1(0,100{about},nil);
     {ShowWindow(Wnd,SW_Hide);}
    end;
    WM_RButtonDown: begin
     m:=LoadMenu(Seg(HInstance),MakeIntResource(100));
     sysm:=GetSubMenu(m,0);
     GetCursorPos(P);
     TrackPopupMenu(sysm,TPM_RightAlign or TPM_RightButton,
      P.x,P.y,0,Wnd,nil);
     DestroyMenu(m);
    end;
   end;
   WM_Command: case wParam of		{kommen nur vom System-Tray!}
    2: SendMessage(Wnd,WM_Close,0,0);
    100: begin
     ShowWindow(Wnd,SW_Minimize);
     Shell_NotifyIcon(NIM_Delete,traydata);
    end;
    900: WinHelp(Wnd,HelpFileName,HELP_Index,0);
    999: MBox1(Wnd,100{about},nil);
   end;
   WM_Close: begin
    if (DdeConnections>0)
    and (MBox1(Wnd,109{WarnClose},PChar(DdeConnections))<>IDYes)
    then exit;
    if LongRec(traydata.cbSize).lo<>0
    then Shell_NotifyIcon(NIM_Delete,traydata);
    WinHelp(Wnd,HelpFileName,HELP_Quit,0);
    DestroyWindow(Wnd);
   end;
   WM_Destroy: PostQuitMessage(0);
   else calldef:=true;
  end;
  if calldef then MainWndProc:=DefWindowProc(Wnd,Msg,wParam,lParam);
 end;

const
 wc: TWndClass=(
  style: CS_VRedraw or CS_HRedraw;
  lpfnWndProc: @MainWndProc;
  cbClsExtra: 0;
  cbWndExtra: 0;
  hInstance: Seg(HInstance);
  hIcon: 0;
  hCursor: 0;
  hbrBackground: COLOR_Window;
  lpszMenuName: nil;
  lpszClassName: AppName);
var
 Msg:TMsg;
begin
 if HPrevInst<>0 then begin
  wc.hIcon:=FindWindow(AppName,nil);
  if wc.hIcon<>0 then begin
   SetActiveWindow(wc.hIcon);
  end;
  exit;
 end;
 wc.hIcon:=LoadIcon(Seg(HInstance),MakeIntResource(100));
 wc.hCursor:=LoadCursor(0,IDC_Arrow);
 RegisterClass(wc);
 MainWnd:=CreateWindow(AppName,AppTitle,
   WS_OverlappedWindow,
   0,0,0,0,
   0,0,Seg(HInstance),nil);
 ShowWindow(MainWnd,SW_ShowMinimized);
 WUtils.StdMBoxTitle:=AppTitle;
 CF_XlTable:=RegisterClipboardFormat('XlTable');
 CF_MatTable:=RegisterClipboardFormat('MatTable');
 DdeInit;
 while GetMessage(Msg,0,0,0) do begin
  TranslateMessage(Msg);
  DispatchMessage(Msg);
 end;
 DdeDone;
end.
Vorgefundene Kodierung: UTF-80