FoxPro

FoxPro Developer's Conference '94

Session 241
DDE/OLE mit FoxPro

Kurt Zander
Papst GmbH


 

DDEData - Einfacher FoxPro DDE-Server

#DEFINE		sCRLF		CHR(13) + CHR(10)
#DEFINE		sTAB		CHR(9)
#DEFINE   	SVR_NAME	"DDEData"
** Alle Elemente sind mit .F. vorbelegt
PUBLIC ARRAY aiCh[100]						&& Array für die Kanäle
  ** Einrichten des Servers mittels eines Dienstnamens, hier (und meistens)
  ** der Name der EXE-Datei. Der Dienstname darf maximal 8 Zeichen lang sein
  =DDESetService(SVR_NAME, "define")
  
  ** als nächstes kommen die Angaben der unterstützten DDE-Aktionen des
  ** Servers
  
  ** Sucht Daten oder führt, wie in diesem Programm, eine Abfrage
  ** des Client aus. Ist eine Abfrage erfolgreich, wird die Funktion 
  ** DDEPoke() zum Senden der Daten an die andere Anwendung benutzt
  =DDESetService(SVR_NAME, "request", .T.)
  ** Aktualisiert das angegebene Element - hier den Datensatz - mit dem
  ** übergebenen Wert
  =DDESetService(SVR_NAME, "poke", .T.)
  
  ** Erlaubt die Ausführung des übergebenen FoxPro-Befehles - hier nicht
  ** vorgesehen
  =DDESetService(SVR_NAME, "execute", .T.)
  
  ** Verwendet die Funktion DDEPoke() zum Informieren des Clients über
  ** Änderungen oder zum Senden aktualisierter Daten an die andere An-
  ** wendung, wenn sich das Element (z.B. der Datensatz) geändert hat
  =DDESetService(SVR_NAME, "advise", .F.)
  ** Einrichten des Themas für den Client. Neben dem Dienstnamen wird das
  ** Thema (Topic) angegeben, mit dem die andere Anwendung die Konversa-
  ** tion starten kann. Hier wird eine allgemeine Rückruffunktion einge-
  ** richtet, so daß das Thema als Nullstring übergeben wird.
  ** Der 3. Parameter bestimmt den Namen der Rückruffunktion, also der
  ** Routine, die die Konversation auswertet und Stati oder Daten zurück-
  ** gibt. Wird der dritte Parameter weggelassen, wird das Thema freige-
  ** geben.
  =DDESetTopic(SVR_NAME, "", "cbDataTopic")
  ** in einem Cleanup-Bereich muß auf jeden Fall mit der Funktion
  ** 			DDESetService("DDEData", "release")
  ** der Dienst beendet werden
RETURN
** EoP DDEData
****************************************************************************
* cbDataTopic - Rückruf für Tabellen-, Abfrage- oder SQL-Themen
*
* Gültige Themen:
*		<database>;TABLE <table name>
*
* z.B. "D:\ADRESSEN;TABLE Adressen.Dbf"
*
* Andere Themen verursachen ein Fehlschlagen von INITIATE
*
* Gültige Elemente:
*		All - alle Daten, einschließlich Feldnamen
*		Data - alle Daten, ohne Feldnamen
*		FieldNames - eine Liste der Feldnamen
*		NextRow - die nächste Zeile Daten
*		PrevRow - die vorherige Zeile Daten
*		FirstRow - die erste Zeile Daten
*		Lastrow - die letzte Zeile Daten
*		FieldCount - die Anzahl der Felder in der Tabelle
*		nn - Zurückzugebende Zeilennummer
*		mm-nn Zurückzugebender Zeilenbereich
*
* Eine Sendeanforderung (DDEPoke) erwartet den Feldnamen als
* das Element und die neuen Daten für dieses Feld. 
* Andere Sendeanforderungen werden nicht ausgeführt.
****************************************************************************
PROCEDURE cbDataTopic
  **-----------------------------------------------------------------
  ** iChannel = die Kanalnummer des Client
  ** sAction  = die Aktionsmöglichkeiten INITIATE, TERMINATE,
  **    REQUEST, EXECUTE, ADVISE, POKE
  ** sItem    = der Elementenname (siehe oben unter gültige Elemente)
  ** sData    = die Daten vom Clienten
  ** sFormat  = das Format der Daten (z.B. CF_TEXT, CF_BITMAP usw.)
  ** iStatus  = der Verknüpfungtyp (hotlink, coldlink)
  **-----------------------------------------------------------------
  PARAMETERS iChannel, sAction, sItem, sData, sFormat, iStatus
  PRIVATE    bResult, sDatabase, sRowType, sRowSrc, iTemp, sUpItem, sUpData
  PRIVATE    sResult, iTemp2, bEnabState
  PRIVATE    sTagTable, sTagSQL
  ** Diese Funktion aktiviert oder deaktiviert den weiteren DDE.
  ** Hier wird eine weitere Anfrage deaktiviert, bis die Bearbeitung der
  ** Anfrage erfolgt ist. Am Ende der Routine erfolgt die Aktivierung
  =DDEEnabled(.F.)					
  sTagTable	= ";TABLE"					&& muß in Großbuchstaben sein
  bResult 	= .T.						&& assume Aktion erfolgreich
  DO CASE
  **------------------------------------------------------------------------
  ** Client hat ein Initiate abgesetzt (bei FoxPro selbst das DDEInitiate())
  ** Es wird nunmehr ausgewertet, ob im einzigen gültigen Thema
  ** 1. der Inhalt der Var sTagTable vorhanden ist
  ** 2. ob evtl. eine Pfadangabe vorhanden ist und
  ** 3. ob die angegebene Tabelle vorhanden ist.
  **------------------------------------------------------------------------
  CASE sAction = "INITIATE"			
	sUpData = UPPER(sData)					&& immer Großbuchstaben
	IF (!sTagTable $ sUpData)				&& Thementeil nicht vorhanden?
	  bResult = .F.					&& Verbindung ist fehlgeschlagen
	ELSE
	  iTemp     = AT(sTagTable, sUpData)			&& Position des Thementeils
	  sDatabase = LTRIM(SUBSTR(sUpData, ;
	   1, iTemp-1))		&& Pfadangabe
      IF (!EMPTY(sDatabase))					&& Pfadname vorhanden?
	    SET DEFAULT TO (sDatabase)			&& Verzeichnis setzen
      ENDIF
		
	  sRowSrc = ALLTRIM(SUBSTR(sData, ;
	   iTemp + ;
	   LEN(sTagTable)))	&& Name der Tabelle
	  
	  IF (!(FILE(sRowSrc) OR ;
	FILE(sRowSrc+".DBF")))			&& Tabelle nicht vorhanden?
		bResult = .F.					&& Verbindung ist fehlgeschlagen
	  ELSE
		** Festhalten der von den verschiedenen DDE-Kanälen 
		** genutzten Arbeitsbereiche.
		iTemp = ASCAN(aiCh, .F.)			&& nächster freier Kanal
		IF (iTemp = 0)					&& keiner mehr möglich?
		  bResult = .F.				&& Verbindung ist fehlgeschlagen
		ELSE						&& ist noch was frei
		  aiCh[iTemp] = iChannel			&& Kanalnummer vom Client
		  USE (sRowSrc) IN (iTemp) AGAIN		&& öffne Tabelle
		ENDIF
	  ENDIF
	ENDIF
  **------------------------------------------------------------------------
  ** Der Client hat eine Anfrage abgesetzt. In den meisten Programmen
  ** geschieht das durch ein DDE-Peek() oder bei FoxPro selbst durch die
  ** Funktion DDERequest(). Was abgefragt werden soll, wird durch den
  ** Parameter sItem (Elementenname) festgelegt.
  **------------------------------------------------------------------------
  CASE (sAction = "REQUEST")
	iTemp = ASCAN(aiCh, iChannel)				&& welcher Arbeitsbereich?
	IF (iTemp != 0)					&& ist dieser vorhanden?
	  SELECT (iTemp)					&& selektiere diesen
	ENDIF
	
	sUpItem = UPPER(sItem)					&& in Großbuchstaben
	
	DO CASE							&& werte aus, welches Element
	CASE (sUpItem = "FIELDNAMES")
	  sResult = sFldNames()				&& ermittle die Feldnamen
		
	CASE (sUpItem = "FIELDCOUNT")			
	  sResult = sFldCount()				&& Anzahl der Felder
		
	CASE (sUpItem = "FIRSTROW")
	  GO TOP						&& zum Tabellenanfang
	  sResult = sRowData()					&& Daten als String aufbereitet
		
	CASE (sUpItem = "LASTROW")
	  GO BOTTOM						&& zum Tabellenende
	  sResult = sRowData()					&& Daten als String aufbereitet
		
	CASE (sUpItem = "NEXTROW")
	  IF (EOF())						&& Tabellenende erreicht
		bResult = .F.					&& Anfrage schlägt fehl
	  ELSE							&& sonst
		SKIP						&& nächster Satz
		sResult = sRowData()				&& Daten als String aufbereitet
	  ENDIF
		
	CASE (sUpItem = "PREVROW")
	  IF (BOF())						&& Tabellenanfang erreicht?
	    bResult = .F.					&& Anfrage schlägt fehl
	  ELSE							&& sonst
		SKIP -1						&& vorheriger Satz
		sResult = sRowData()				&& Daten als String aufbereitet
	  ENDIF
		
	CASE (sUpItem = "ALL")
	  sResult = sFldNames() + sAllData()			&& Feldnamen und alle Daten
		
	CASE (sUpItem = "DATA")
	  sResult = sAllData()					&& alle Daten
		
	CASE (VAL(sUpItem) <> 0)				&& eine Satzgruppe oder ein Satz
	  iFirst = MAX(1, ;
	       MIN(INT(VAL(sUpItem)), ;
	   RECCOUNT()))			&& ermittle die 1. Satznummer
	  IF ("-" $ sUpItem)					&& Von-Bis übergeben?
		iLast   = VAL(SUBSTR(sUpItem, ;
		      AT("-", sUpItem)+1))
		iLast   = MAX(1, ;
		      MIN(iLast, RECCOUNT()))
		iLast   = MAX(iFirst, iLast)			&& ermittle ersten und letzten
		sResult = ""					&& Rückgabestring
		
		GO iFirst					&& gehe zum "ersten"
		FOR iTemp = iFirst TO iLast			&& arbeite Von - Bis ab
  		  sResult = sResult + sRowData()		&& Daten als String aufbereitet
		  SKIP						&& nächster Satz
		ENDFOR
	  ELSE							&& nur eine Satznummer übergeben
		GO iFirst					&& gehe dort hin
		sResult = sRowData()				&& Daten als String aufbereitet
	  ENDIF
		
	OTHERWISE						&& alle anderen Anfragen
	  bResult = .F.					&& schlagen ganz einfach fehl
	ENDCASE
	** Die angeforderten Daten an die andere Anwendung senden, wenn die
	** Anfrage erfolgreich war
	IF (bResult)
	  ** Die Parameter sind: die Kanalnummer des Client, der Elementenname
	  ** und das ermittelte Resultat
	  =DDEPoke(iChannel, sItem, sResult)
	ENDIF
  **------------------------------------------------------------------------
  ** Der Client hat ein Ersetzen eines bestimmten Elementes angefordert
  ** In den meisten Programmen erfolgt dies durch eine DDE-Poke() Funktion.
  ** In diesem Falle soll durch Übergabe des Feldnamens (sItem) der Wert
  ** sData in das Feld übertragen werden
  **------------------------------------------------------------------------
  CASE (sAction = "POKE")
	FOR iTemp = 1 TO FCOUNT()				&& evtl. alle Felder testen
	  IF (FIELD(iTemp) = UPPER(sItem))			&& der übergebene Feldname?
		iTemp = 0					&& wegen der unteren Prüfung
		REPLACE &sItem WITH ;
		vCvtField(TYPE('&sItem'), ;
		  sData)			&& ersetze Feldinhalt
		EXIT						&& verlasse Schleife
  	  ENDIF
	ENDFOR
	
	IF (iTemp != 0)					&& Feld nicht vorhanden?
	  bResult = .F.					&& Anforderung ist fehlgeschlagen
	ENDIF
	
  **------------------------------------------------------------------------
  ** Der Client will die Verbindung zum übergegebenen Kanal aufheben.
  ** In den meisten Programmen erfolgt dies durch eine DDE-Terminate() Funk-
  ** tion.
  **------------------------------------------------------------------------
  CASE (sAction = "TERMINATE")
	iTemp = ASCAN(aiCh, iChannel)				&& Kanal im Array eingetragen?
	IF (iTemp != 0)					&& gefunden?
      	  SELECT (iTemp)					&& selektiere Arbeitsbereich
	  USE							&& schließe Tabelle
	  aiCh[iTemp] = .F.					&& Arrayelement zurücksetzen
	ENDIF
	
  **------------------------------------------------------------------------
  ** Alle anderen Anfragen oder Anforderungen des Clients führen zum Fehl-
  ** schlag
  **------------------------------------------------------------------------
  OTHERWISE
	bResult = .F.
  ENDCASE
  =DDEEnabled(.T.)						&& nächste Anfrage wieder möglich
RETURN (bResult)						&& das war es
** EoFu cbDataTopic()
***********************************************************************
*  Hilfsfunktionen, die von cbDataTopic() aktiviert werden
***********************************************************************
FUNCTION sFldNames
  PRIVATE sResult, iTemp
  sResult = ""
  
  FOR iTemp = 1 TO FCOUNT()
	sResult = sResult ;
	+ FIELD(iTemp) ;
	+ sTAB						&& jeweils mit TAB terminiert
  ENDFOR
  
  sResult = SUBSTR(sResult, 1, LEN(sResult)-1) ;
  + sCRLF						&& alle Felder mit CrLf termin.
RETURN (sResult)
** EoFu sFldNames()
FUNCTION sFldCount
  PRIVATE sResult
  sResult = STR(FCOUNT())+sCRLF
RETURN (sResult)
** EoFu sFldCount()
FUNCTION sRowData
  PRIVATE sResult, iTemp, avData
  SCATTER MEMO TO avData
  sResult = ""
  
  FOR iTemp = 1 TO FCOUNT()
	sResult = sResult ;
	+ sCvtField(TYPE('avData[iTemp]'), avData[iTemp]) ;
	+ sTAB
  ENDFOR
  
  sResult = SUBSTR(sResult, 1, LEN(sResult)-1) ;
  + sCRLF
RETURN (sResult)
** EoFu sRowData()
FUNCTION sAllData
  PRIVATE sResult
  sResult = ""
  SCAN
	sResult = sResult ;
	+ sRowData()
  ENDSCAN
RETURN (sResult)
** EoFu sAllData()
FUNCTION sCvtField
  PARAMETERS sType, vValue
  PRIVATE	 sTemp
  DO CASE
  CASE (INLIST(sType, 'C', 'M'))
	sTemp = vValue
  CASE (INLIST(sType, 'N', 'F'))
	sTemp = ALLTRIM(STR(vValue,15))
  CASE (sType = 'D')
	sTemp = DTOC(vValue)
  CASE (sType = 'L')
	sTemp = IIF(vValue, 'JA', 'NEIN')
  ENDCASE
RETURN (sTemp)
** EoFu sCvtField()
FUNCTION vCvtField
  PARAMETERS sType, sValue
  PRIVATE    vTemp
  DO CASE
  CASE (INLIST(sType, 'C', 'M'))
	vTemp = sValue
  CASE (INLIST(sType, 'N', 'F'))
	vTemp = VAL(sValue)
  CASE (sType = 'D')
	vTemp = CTOD(sValue)
  CASE (sType = 'L')
	vTemp = IIF(UPPER(ALLTRIM(sValue)) $ 'JA', .T., .F.)
  ENDCASE
RETURN (vTemp)
** EoFu vCvtField()
** EoF DDEDATA.PRG  **

DDE/OLE mit FoxPro
(c)1994 Kurt Zander