FoxProFoxPro 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