Author: Embarcadero USA
Technical Information Database TI1177D.txt - DDE: A simple example Category :Delphi 1.x Platform :All Windows Product : Description: Q: How can I do DDE under Delphi using API calls ? A: Its fairly easy to accomplish, following is an example of how to have a client program talk with a server program. Both are completely written in Delphi. In total there are 2 projects, 3 forms, and 3 units. This demo uses DDE ML API methods to handle the DDE requests. The server must be running before the client will load. This demo program shows 3 different ways data can be moved between a client and a server. 1. The Client can 'POKE' data to the server. 2. The Server can automaticaly pass data to the Client and the Client will update a graph based on the results from the Server. 3. The Server's Data changes, then the Client will make a request to the Server for the new data, then update the graph. ***** How to handle the program. ***** Following are 8 files concatenated together. Each one has a { *** BEGIN CODE FOR FILENAME.EXT *** } CODE { *** END CODE FOR FILENAME.EXT *** } take each block of code BETWEEN THE { *** } lines and place in a file of the corresponding name, then compile and have fun !!!! { *** BEGIN CODE FOR DDEMLCLI.DPR *** } program Ddemlcli; uses Forms, Ddemlclu in 'DDEMLCLU.PAS' {Form1}; {$R *.RES} begin Application.CreateForm(TForm1, Form1); Application.Run; end. { *** END CODE FOR DDEMLCLI.DPR *** } { *** BEGIN CODE FOR DDEMLCLU.DFM *** } object Form1: TForm1 Left = 197 Top = 95 Width = 413 Height = 287 HorzScrollBar.Visible = False VertScrollBar.Visible = False Caption = 'DDEML Demo, Client Application' Font.Color = clWindowText Font.Height = -13 Font.Name = 'System' Font.Style = [] Menu = MainMenu1 PixelsPerInch = 96 OnCreate = FormCreate OnDestroy = FormDestroy OnShow = FormShow TextHeight = 16 object PaintBox1: TPaintBox Left = 0 Top = 0 Width = 405 Height = 241 Align = alClient Color = clWhite ParentColor = False OnPaint = PaintBox1Paint end object MainMenu1: TMainMenu Top = 208 object File1: TMenuItem Caption = '&File' object exit1: TMenuItem Caption = 'E&xit' OnClick = exit1Click end end object DDE1: TMenuItem Caption = '&DDE' object RequestUpdate1: TMenuItem Caption = '&Request an Update' OnClick = RequestUpdate1Click end object AdviseofChanges1: TMenuItem Caption = '&Advise of Changes' OnClick = AdviseofChanges1Click end object N1: TMenuItem Caption = '-' end object PokeSomeData: TMenuItem Caption = '&Poke Some Data' OnClick = PokeSomeDataClick end end end end { *** END CODE FOR DDEMLCLU.DFM *** } { *** BEGIN CODE FOR DDEMLCLU.PAS *** } {***************************************************} { } { Delphi 1.0 DDEML Demonstration Program } { Copyright (c) 1996 by Borland International } { } {***************************************************} { This is a sample application demonstrating the use of the DDEML APIs in a client application. It uses the DataEntry server application that is part of this demo in order to maintain a display of the entered data as a bar graph. You must run the server application first (in DDEMLSRV.PAS), and then run this client. If the server is not running, this application will fail trying to connect. The interface to the server is defined by the list of names (Service, Topic, and Items) in the separate unit called DataEntry (DATAENTR.TPU). The server makes the Items available in cf_Text format; they are con- verted and stored locally as integers. } unit Ddemlclu; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, VBXCtrl, ExtCtrls, DDEML, Menus, StdCtrls; const NumValues = 3; type { Data Structure which constitutes a sample } TDataSample = array [1..NumValues] of Integer; TDataString = array [0..20] of Char; { Size of Item as text } { Main Form } TForm1 = class(TForm) MainMenu1: TMainMenu; File1: TMenuItem; exit1: TMenuItem; DDE1: TMenuItem; RequestUpdate1: TMenuItem; AdviseofChanges1: TMenuItem; PokeSomeData: TMenuItem; N1: TMenuItem; PaintBox1: TPaintBox; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure RequestUpdate1Click(Sender: TObject); procedure FormShow(Sender: TObject); procedure AdviseofChanges1Click(Sender: TObject); procedure PokeSomeDataClick(Sender: TObject); procedure Request(HConversation: HConv); procedure exit1Click(Sender: TObject); procedure PaintBox1Paint(Sender: TObject); private { Private declarations } public Inst: Longint; CallBackPtr: ^TCallback; ServiceHSz : HSz; TopicHSz : HSz; ItemHSz : array [1..NumValues] of HSz; ConvHdl : HConv; DataSample : TDataSample; end; var Form1: TForm1; implementation const DataEntryName : PChar = 'DataEntry'; DataTopicName : PChar = 'SampledData'; DataItemNames : array [1..NumValues] of pChar = ('DataItem1', 'DataItem2', 'DataItem3'); {$R *.DFM} { Local Function: CallBack Procedure for DDEML } function CallbackProc(CallType, Fmt: Word; Conv: HConv; hsz1, hsz2: HSZ; Data: HDDEData; Data1, Data2: Longint): HDDEData; export; begin CallbackProc := 0; { See if proved otherwise } case CallType of xtyp_Register: begin { Nothing ... Just return 0 } end; xtyp_Unregister: begin { Nothing ... Just return 0 } end; xtyp_xAct_Complete: begin { Nothing ... Just return 0 } end; xtyp_Request, Xtyp_AdvData: begin Form1.Request(Conv); CallbackProc := dde_FAck; end; xtyp_Disconnect: begin ShowMessage('Disconnected!'); Form1.Close; end; end; end; { Posts a DDE request to obtain cf_Text data from the server. Requests the data for all fields of the DataSample, and invalidates the window to cause the new data to be displayed. Obtains the data from the Server synchronously, using DdeClientTransaction. } procedure TForm1.Request(HConversation: HConv); var hDdeTemp : HDDEData; DataStr : TDataString; Err, I : Integer; begin if HConversation <> 0 then begin for I := Low(ItemHSz) to High(ItemHSz) do begin hDdeTemp := DdeClientTransaction(nil, 0, HConversation, ItemHSz[I], cf_Text, xtyp_Request, 0, nil); if hDdeTemp <> 0 then begin DdeGetData(hDdeTemp, DataStr, SizeOf(DataStr), 0); Val(DataStr, DataSample[I], Err); end; { if } end; { for } Paintbox1.Refresh; { Redisplay the Screen } end; { if } end; procedure TForm1.FormCreate(Sender: TObject); var I : Integer; { Constructs an instance of the DDE Client Window. Constructs the window using the inherited constructor, then initializes the instance data. } begin Inst := 0; { Must be zero for first call to DdeInitialize } CallBackPtr:= nil; { MakeProcInstance is called in SetupWindow } ConvHdl := 0; ServiceHSz := 0; TopicHSz := 0; for I := Low(DataSample) to High(DataSample) do begin ItemHSz[I] := 0; DataSample[I] := 0; end; end; procedure TForm1.FormDestroy(Sender: TObject); { Destroys an instance of the Client window. Frees the DDE string handles, and frees the callback proc instance if they exist. Also calls DdeUninitialize to terminate the conversation. Then calls on the ancestral destructor to finish the job. } var I : Integer; begin if ServiceHSz <> 0 then DdeFreeStringHandle(Inst, ServiceHSz); if TopicHSz <> 0 then DdeFreeStringHandle(Inst, TopicHSz); for I := Low(ItemHSz) to High(ItemHSz) do if ItemHSz[I] <> 0 then DdeFreeStringHandle(Inst, ItemHSz[I]); if Inst <> 0 then DdeUninitialize(Inst); { Ignore the return value } if CallBackPtr <> nil then FreeProcInstance(CallBackPtr); end; procedure TForm1.RequestUpdate1Click(Sender: TObject); begin { Generate a DDE Request in response to the DDE | Request menu selection.} Request(ConvHdl); end; procedure TForm1.FormShow(Sender: TObject); { Completes the initialization of the DDE Server Window. Performs those actions which require a valid window. Initializes the use of the DDEML. } var I : Integer; InitOK: Boolean; begin CallBackPtr := MakeProcInstance( CallBackProc, HInstance); { Initialize the DDE and setup the callback function. If server is not present, call will fail. } if CallBackPtr <> nil then begin if DdeInitialize(Inst, TCallback(CallBackPtr), AppCmd_ClientOnly, 0) = dmlErr_No_Error then begin ServiceHSz:= DdeCreateStringHandle(Inst, DataEntryName, cp_WinAnsi); TopicHSz := DdeCreateStringHandle(Inst, DataTopicName, cp_WinAnsi); InitOK := True; { for I := Low(DataItemNames) to High(DataItemNames) do begin } for I := 1 to NumValues do begin ItemHSz[I]:= DdeCreateStringHandle(Inst, DataItemNames[I], cp_WinAnsi); InitOK := InitOK and (ItemHSz[I] <> 0); end; if (ServiceHSz <> 0) and (TopicHSz <> 0) and InitOK then begin ConvHdl := DdeConnect(Inst, ServiceHSz, TopicHSz, nil); if ConvHdl = 0 then begin ShowMessage('Can not start Conversation!'); Close; end end else begin ShowMessage('Can not create Strings!'); Close; end end else begin ShowMessage('Can not Initialie!'); Close; end; end; end; procedure TForm1.AdviseofChanges1Click(Sender: TObject); { Toggles the state of the DDE Advise setting in response to the DDE | Advise menu selection. When this is selected, all three Items are set for Advising. } var I: Integer; TransType: Word; TempResult: Longint; begin with TMenuITem(Sender) do begin Checked := not Checked; if Checked then TransType:= (xtyp_AdvStart or xtypf_AckReq) else TransType:= xtyp_AdvStop; end; { with } for I := Low(ItemHSz) to High(ItemHSz) do if DdeClientTransaction(nil, 0, ConvHdl, ItemHSz[I], cf_Text, TransType, 1000, TempResult) = 0 then ShowMessage('Can not perform Advise Transaction'); if TransType and xtyp_AdvStart <> 0 then Request(ConvHdl); end; procedure TForm1.PokeSomeDataClick(Sender: TObject); { Generates a DDE Poke transaction in response to the DDE | Poke menu selection. Requests a value from the user that will be poked into DataItem1 as an illustration of the Poke function. } var DataStr: pChar; S: String; begin S := '0'; if InputQuery('PokeData', 'Enter Value to Poke', S) then begin S := S + #0; DataStr := S[1]; DdeClientTransaction(DataStr, StrLen(DataStr) + 1, ConvHdl, ItemHSz[1], cf_Text, xtyp_Poke, 1000, nil); Request(ConvHdl); end; end; procedure TForm1.exit1Click(Sender: TObject); begin close; end; procedure TForm1.PaintBox1Paint(Sender: TObject); { Repaints the window on request. Plots a graph of the current sales volume. } const LMarg = 30; { Left Margin of graph } var I, Norm: Integer; Wd: Integer; Step : Integer; ARect: TRect; begin Norm := 0; for I := Low(DataSample) to High(DataSample) do begin if abs(DataSample[I]) > Norm then Norm := abs(DataSample[I]); end; { for } if Norm = 0 then Norm := 1; { Just in case we have all zeros } with TPaintBox(Sender).Canvas do begin { Paint Background } Brush.color := clWhite; FillRect(ClipRect); { Draw Axis } MoveTo(0, ClipRect.Bottom div 2); LineTo(ClipRect.Right, ClipRect.Bottom div 2); MoveTo(LMarg, 0); LineTo(LMarg, ClipRect.Bottom); { Print Left MArgin Text } TextOut(0,0, IntToStr(Norm)); TextOut(0, ClipRect.Bottom div 2, '0'); TextOut(0, ClipRect.Bottom + Font.Height, IntToStr(-Norm)); TextOut(0, ClipRect.Bottom div 2, '0'); TextOut(0, ClipRect.Bottom div 2, '0'); TextOut(0, ClipRect.Bottom div 2, '0'); { Print X Axis Text } { Now draw the bars based on that Normalized value. Compute the width of the bars so that all will fit in the window, and compute an inter-bar space that is approximately 20% of the width of a bar. } { SelectObject(PaintDC, CreateSolidBrush(RGB(255, 0, 0))); SetBkMode(PaintDC, Transparent); } ARect := ClipRect; Wd := (ARect.Right - LMarg) div NumValues; Step := Wd div 5; Wd := Wd - Step; with ARect do begin Left := LMarg + (Step div 2); Top := ClipRect.Bottom div 2; end; { with } { Display Bars and X-Axis Text } For i := Low(DataSample) to High(DataSample) do begin with ARect do begin Right := Left + Wd; Bottom:= Top - Round((Top-5) * (DataSample[I] / Norm)); end; { with } { Fill Bar } Brush.color := clFuchsia; FillRect(ARect); { Display Text - Horizontal Axis } Brush.color := clWhite; TextOut(ARect.Left, ClipRect.Bottom div 2 - Font.Height, StrPas(DataItemNames[i])); with ARect do Left := Left + Wd + Step; end; { for } end; { with } end; end.{ *** END CODE FOR DDEMLCLU.PAS *** } { *** BEGIN CODE FOR DDEMLSVR.DPR *** } program Ddemlsvr; uses Forms, Ddesvru in 'DDESVRU.PAS' {Form1}, Ddedlg in '\DELPHI\BIN\DDEDLG.PAS' {DataEntry}; {$R *.RES} begin Application.CreateForm(TForm1, Form1); Application.CreateForm(TDataEntry, DataEntry); Application.Run; end. { *** END CODE FOR DDEMLSVR.DPR *** } { *** BEGIN CODE FOR DDESVRU.DFM *** } object Form1: TForm1 Left = 712 Top = 98 Width = 307 Height = 162 Caption = 'DDEML Demo, Serve Application' Color = clWhite Font.Color = clWindowText Font.Height = -13 Font.Name = 'System' Font.Style = [] Menu = MainMenu1 PixelsPerInch = 96 OnCreate = FormCreate OnDestroy = FormDestroy OnShow = FormShow TextHeight = 16 object Label1: TLabel Left = 0 Top = 0 Width = 99 Height = 16 Caption = 'Current Values:' end object Label2: TLabel Left = 16 Top = 24 Width = 74 Height = 16 Caption = 'Data Item1:' end object Label3: TLabel Left = 16 Top = 40 Width = 74 Height = 16 Caption = 'Data Item2:' end object Label4: TLabel Left = 16 Top = 56 Width = 74 Height = 16 Caption = 'Data Item3:' end object Label5: TLabel Left = 0 Top = 88 Width = 265 Height = 16 Caption = 'Select Data|Enter Data to change values.' end object Label6: TLabel Left = 96 Top = 24 Width = 8 Height = 16 Caption = '0' end object Label7: TLabel Left = 96 Top = 40 Width = 8 Height = 16 Caption = '0' end object Label8: TLabel Left = 96 Top = 56 Width = 8 Height = 16 Caption = '0' end object MainMenu1: TMainMenu Left = 352 Top = 24 object File1: TMenuItem Caption = '&File' object Exit1: TMenuItem Caption = '&Exit' OnClick = Exit1Click end end object Data1: TMenuItem Caption = '&Data' object EnterData1: TMenuItem Caption = '&Enter Data' OnClick = EnterData1Click end object Clear1: TMenuItem Caption = '&Clear' OnClick = Clear1Click end end end end { *** END CODE FOR DDESVRU.DFM *** } { *** BEGIN CODE FOR DDESVRU.PAS *** } {***************************************************} { } { Delphi 1.0 DDEML Demonstration Program } { Copyright (c) 1996 by Borland International } { } {***************************************************} { This sample application uses the DDEML library in the server side of a cooperative application. This server is a simple data-entry application which allows an operator to enter three data items, which are made available through DDE to interested clients. This server makes its service available under the following names: Service: 'DataEntry' Topic : 'SampledData' Items : 'DataItem1', 'DataItem2', 'DataItem3' Conceivably, other topics under this service could be defined. Things such as historical data, information about the sampling, and so on might make useful topics. You must run this server BEFORE running the client (DDEMLCLI.PAS), or the client will fail the connection. The interface to this server is defined by the list of names (Service, Topic, and Items) in the separate unit called DataEntry (DATAENTR.TPU). The server makes the Items available in cf_Text format; they can be converted and stored locally as integers by the client. } unit Ddesvru; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Menus, DDEML, { DDE APi } ShellApi; const NumValues = 3; DataItemNames : array [1..NumValues] of PChar = ('DataItem1', 'DataItem2', 'DataItem3'); type TDataString = array [0..20] of Char; { Size of Item as text } TDataSample = array [1..NumValues] of Integer; {type { Data Structure which constitutes a sample } { TDataSample = array [1..NumValues] of Integer; { TDataString = array [0..20] of Char; { Size of Item as text } const DataEntryName: PChar = 'DataEntry'; DataTopicName: PChar = 'SampledData'; type TForm1 = class(TForm) MainMenu1: TMainMenu; File1: TMenuItem; Exit1: TMenuItem; Data1: TMenuItem; EnterData1: TMenuItem; Clear1: TMenuItem; Label1: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel; Label5: TLabel; Label6: TLabel; Label7: TLabel; Label8: TLabel; procedure Exit1Click(Sender: TObject); function MatchTopicAndService(Topic, Service: HSz): Boolean; function MatchTopicAndItem(Topic, Item: HSz): Integer; function WildConnect(Topic, Service: HSz; ClipFmt: Word): HDDEData; function AcceptPoke(Item: HSz; ClipFmt: Word; Data: HDDEData): Boolean; function DataRequested(TransType: Word; ItemNum: Integer; ClipFmt: Word): HDDEData; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormShow(Sender: TObject); procedure EnterData1Click(Sender: TObject); procedure Clear1Click(Sender: TObject); private Inst : Longint; CallBack : TCallback; ServiceHSz : HSz; TopicHSz : HSz; ItemHSz : array [1..NumValues] of HSz; ConvHdl : HConv; Advising : array [1..NumValues] of Boolean; DataSample : TDataSample; public { Public declarations } end; var Form1: TForm1; implementation uses DDEDlg; { DataEntry Form } {$R *.DFM} procedure TForm1.Exit1Click(Sender: TObject); begin Close; end; { Initialized globals } const DemoTitle : PChar = 'DDEML Demo, Server Application'; MaxAdvisories = 100; NumAdvLoops : Integer = 0; { Local Function: CallBack Procedure for DDEML } { This callback procedure responds to all transactions generated by the DDEML. The target Window object is obtained from the stored global, and the appropriate methods within that objects are used to respond to the given transaction, as indicated by the CallType parameter. } function CallbackProc(CallType, Fmt: Word; Conv: HConv; HSz1, HSz2: HSZ; Data: HDDEData; Data1, Data2: Longint): HDDEData; export; var ItemNum : Integer; begin CallbackProc := 0; { See if proved otherwise } case CallType of xtyp_WildConnect: CallbackProc := Form1.WildConnect(HSz1, HSz2, Fmt); xtyp_Connect: if Conv = 0 then begin if Form1.MatchTopicAndService(HSz1, HSz2) then CallbackProc := 1; { Connected! } end; { When a connection is confirmed, record the conversation handle as the window's own. } xtyp_Connect_Confirm: Form1.ConvHdl := Conv; { The client has requested data, either as a direct request or in response to an advisory. Return the current state of the data. } xtyp_AdvReq, xtyp_Request: begin ItemNum := Form1.MatchTopicAndItem(HSz1, HSz2); if ItemNum > 0 then CallbackProc := Form1.DataRequested(CallType, ItemNum, Fmt); end; { Respond to Poke requests ... this demo only allows Pokes of DataItem1. Return dde_FAck to acknowledge the receipt, 0 otherwise. } xtyp_Poke: begin if Form1.AcceptPoke(HSz2, Fmt, Data) then CallbackProc := dde_FAck; end; { The client has requested the start of an advisory loop. Note that we assume a "hot" loop. Set the Advising flag to indicate the open loop, which will be checked whenever the data is changed. } xtyp_AdvStart: begin ItemNum := Form1.MatchTopicAndItem(HSz1, HSz2); if ItemNum > 0 then begin if NumAdvLoops 0 then begin if NumAdvLoops > 0 then begin Dec(NumAdvLoops); if NumAdvLoops = 0 then Form1.Advising[ItemNum] := False; CallbackProc := 1; end; end; end; end; { Case CallType } end; { Returns True if the given Topic and Service match those supported by this application. False otherwise. } function TForm1.MatchTopicAndService(Topic, Service: HSz): Boolean; begin Result := False; if DdeCmpStringHandles(TopicHSz, Topic) = 0 then if DdeCmpStringHandles(ServiceHSz, Service) = 0 then Result := True; end; { Determines if the given Topic and Item match one supported by this application. Returns the Item Number of the supported item (in the range 1..NumValues) if one is found, and zero if no match. } function TForm1.MatchTopicAndItem(Topic, Item: HSz): Integer; var I : Integer; begin Result := 0; if DdeCmpStringHandles(TopicHSz, Topic) = 0 then for I := 1 to NumValues do if DdeCmpStringHandles(ItemHSz[I], Item) = 0 then Result := I; end; { Responds to wildcard connect requests. These requests are generated whenever a client tries to connect to a server with either service or topic name set to 0. If a server detects a wild card match, it returns a handle to an array of THSZPair's containing the matching supported Service and Topic. } function TForm1.WildConnect(Topic, Service: HSz; ClipFmt: Word): HDDEData; var TempPairs: array [0..1] of THSZPair; Matched : Boolean; begin TempPairs[0].hszSvc := ServiceHSz; TempPairs[0].hszTopic:= TopicHSz; TempPairs[1].hszSvc := 0; { 0-terminate the list } TempPairs[1].hszTopic:= 0; Matched := False; if (Topic= 0) and (Service = 0) then Matched := True { Complete wildcard } else if (Topic = 0) and (DdeCmpStringHandles(Service, ServiceHSz) = 0) then Matched := True else if (DdeCmpStringHandles(Topic, TopicHSz) = 0) and (Service = 0) then Matched := True; if Matched then WildConnect := DdeCreateDataHandle(Inst, TempPairs, SizeOf(TempPairs), 0, 0, ClipFmt, 0) else WildConnect := 0; end; { Accepts and acts upon Poke requests from the Client. For this demonstration, allows only the value of DataItem1 to be changed by a Poke. } function TForm1.AcceptPoke(Item: HSz; ClipFmt: Word; Data: HDDEData): Boolean; var DataStr : TDataString; Err : Integer; TempSample: Integer; begin if (DdeCmpStringHandles(Item, ItemHSz[1]) = 0) and (ClipFmt = cf_Text) then begin DdeGetData(Data, DataStr, SizeOf(DataStr), 0); Val(DataStr, TempSample, Err); if IntToStr(TempSample) <> Label6.Caption then begin Label6.Caption := IntToStr(TempSample); DataSample[1] := TempSample; if Advising[1] then DdePostAdvise(Inst, TopicHSz, ItemHSz[1]); end; AcceptPoke := True; end else AcceptPoke := False; end; { Returns the data requested by the given TransType and ClipFmt values. This could happen either in response to either an xtyp_Request or an xtyp_AdvReq. The ItemNum parameter indicates which of the supported items (in the range 1..NumValues) was requested (note that this method assumes that the caller has already established validity and ID of the requested item using MatchTopicAndItem). The corresponding data from the DataSample instance variable is converted to text and returned. } function TForm1.DataRequested(TransType: Word; ItemNum: Integer; ClipFmt: Word): HDDEData; var ItemStr: TDataString; { Defined in DataEntry.TPU } begin if ClipFmt = cf_Text then begin Str(DataSample[ItemNum], ItemStr); DataRequested := DdeCreateDataHandle(Inst, ItemStr, StrLen(ItemStr) + 1, 0, ItemHSz[ItemNum], ClipFmt, 0); end else DataRequested := 0; end; { Constructs an instance of the DDE Server Window. Calls on the inherited constructor, then sets up this objects own instandce data. } procedure TForm1.FormCreate(Sender: TObject); var I : Integer; begin Inst := 0; { Must be zero for first call to DdeInitialize } CallBack := nil; { MakeProcInstance is called in SetupWindow } for I := 1 to NumValues do begin DataSample[I] := 0; Advising[I] := False; end; { for } end; { Destroys an instance of the DDE Server Window. Checks to see if the Callback Proc Instance had been created, and frees it if so. Also calls DdeUninitialize to terminate the conversation. Then just calls on the ancestral destructor to finish. } procedure TForm1.FormDestroy(Sender: TObject); var I : Integer; begin if ServiceHSz <> 0 then DdeFreeStringHandle(Inst, ServiceHSz); if TopicHSz <> 0 then DdeFreeStringHandle(Inst, TopicHSz); for I := 1 to NumValues do if ItemHSz[I] <> 0 then DdeFreeStringHandle(Inst, ItemHSz[I]); if Inst <> 0 then DdeUninitialize(Inst); { Ignore the return value } if CallBack <> nil then FreeProcInstance( CallBack); end; procedure TForm1.FormShow(Sender: TObject); var I : Integer; { Completes the initialization of the DDE Server Window. Initializes the use of the DDEML by registering the services provided by this application. Recall that the actual names used to register are defined in a separate unit (DataEntry), so that they can be used by the client as well. } begin CallBack:= MakeProcInstance( CallBackProc, HInstance); if DdeInitialize(Inst, CallBack, 0, 0) = dmlErr_No_Error then begin ServiceHSz:= DdeCreateStringHandle(Inst, DataEntryName, cp_WinAnsi); TopicHSz := DdeCreateStringHandle(Inst, DataTopicName, cp_WinAnsi); for I := 1 to NumValues do ItemHSz[I] := DdeCreateStringHandle(Inst, DataItemNames[I], cp_WinAnsi); if DdeNameService(Inst, ServiceHSz, 0, dns_Register) = 0 then ShowMessage('Registration failed.'); end; end; procedure TForm1.EnterData1Click(Sender: TObject); { Activates the data-entry dialog, and updates the stored data when complete. } var I: Integer; begin if DataEntry.ShowModal = mrOk then begin with DataEntry do begin Label6.Caption := S1; Label7.Caption := S2; Label8.Caption := S3; DataSample[1] := StrToInt(S1); DataSample[2] := StrToInt(S2); DataSample[3] := StrToInt(S3); end; { with } for I := 1 to NumValues do if Advising[I] then DdePostAdvise(Inst, TopicHSz, ItemHSz[I]); end; { if } end; procedure TForm1.Clear1Click(Sender: TObject); { Clears the current data. } var I: Integer; begin for I := 1 to NumValues do begin DataSample[I] := 0; if Advising[I] then DdePostAdvise(Inst, TopicHSz, ItemHSz[I]); end; Label6.Caption := '0'; Label7.Caption := '0'; Label8.Caption := '0'; end; end. { *** END CODE FOR DDESVRU.PAS *** } { *** BEGIN CODE FOR DDEDLG.DFM *** } object DataEntry: TDataEntry Left = 488 Top = 132 ActiveControl = OKBtn BorderStyle = bsDialog Caption = 'Data Entry' ClientHeight = 264 ClientWidth = 199 Font.Color = clBlack Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [fsBold] PixelsPerInch = 96 Position = poScreenCenter OnShow = FormShow TextHeight = 13 object Bevel1: TBevel Left = 8 Top = 8 Width = 177 Height = 201 Shape = bsFrame IsControl = True end object OKBtn: TBitBtn Left = 16 Top = 216 Width = 69 Height = 39 Caption = '&OK' ModalResult = 1 TabOrder = 3 OnClick = OKBtnClick Glyph.Data = { BE060000424DBE06000000000000360400002800000024000000120000000100 0800000000008802000000000000000000000000000000000000000000000000 80000080000000808000800000008000800080800000C0C0C000C0DCC000F0CA A600000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000F0FBFF00A4A0A000808080000000 FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00030303030303 0303030303030303030303030303030303030303030303030303030303030303 03030303030303030303030303030303030303030303FF030303030303030303 03030303030303040403030303030303030303030303030303F8F8FF03030303 03030303030303030303040202040303030303030303030303030303F80303F8 FF030303030303030303030303040202020204030303030303030303030303F8 03030303F8FF0303030303030303030304020202020202040303030303030303 0303F8030303030303F8FF030303030303030304020202FA0202020204030303 0303030303F8FF0303F8FF030303F8FF03030303030303020202FA03FA020202 040303030303030303F8FF03F803F8FF0303F8FF03030303030303FA02FA0303 03FA0202020403030303030303F8FFF8030303F8FF0303F8FF03030303030303 FA0303030303FA0202020403030303030303F80303030303F8FF0303F8FF0303 0303030303030303030303FA0202020403030303030303030303030303F8FF03 03F8FF03030303030303030303030303FA020202040303030303030303030303 0303F8FF0303F8FF03030303030303030303030303FA02020204030303030303 03030303030303F8FF0303F8FF03030303030303030303030303FA0202020403 030303030303030303030303F8FF0303F8FF03030303030303030303030303FA 0202040303030303030303030303030303F8FF03F8FF03030303030303030303 03030303FA0202030303030303030303030303030303F8FFF803030303030303 030303030303030303FA0303030303030303030303030303030303F803030303 0303030303030303030303030303030303030303030303030303030303030303 0303} Margin = 2 NumGlyphs = 2 Spacing = -1 IsControl = True end object CancelBtn: TBitBtn Left = 108 Top = 216 Width = 69 Height = 39 Caption = '&Cancel' TabOrder = 4 Kind = bkCancel Margin = 2 Spacing = -1 IsControl = True end object Panel2: TPanel Left = 16 Top = 88 Width = 153 Height = 49 BevelInner = bvLowered BevelOuter = bvNone TabOrder = 1 object Label1: TLabel Left = 24 Top = 8 Width = 5 Height = 13 end object Label2: TLabel Left = 8 Top = 8 Width = 48 Height = 13 Caption = 'Value 2:' end object Edit2: TEdit Left = 8 Top = 24 Width = 121 Height = 20 MaxLength = 10 TabOrder = 0 Text = '0' end end object Panel1: TPanel Left = 16 Top = 16 Width = 153 Height = 49 BevelInner = bvLowered BevelOuter = bvNone TabOrder = 0 object Label4: TLabel Left = 8 Top = 8 Width = 48 Height = 13 Caption = 'Value 1:' end object Edit1: TEdit Left = 8 Top = 24 Width = 121 Height = 20 MaxLength = 10 TabOrder = 0 Text = '0' end end object Panel3: TPanel Left = 16 Top = 144 Width = 153 Height = 49 BevelInner = bvLowered BevelOuter = bvNone TabOrder = 2 object Label6: TLabel Left = 8 Top = 8 Width = 48 Height = 13 Caption = 'Value 3:' end object Edit3: TEdit Left = 8 Top = 24 Width = 121 Height = 20 MaxLength = 10 TabOrder = 0 Text = '0' end end end { *** END CODE FOR DDEDLG.DFM *** } { *** BEGIN CODE FOR DDEDLG.PAS *** } {***************************************************} { } { Delphi 1.0 DDEML Demonstration Program } { Copyright (c) 1996 by Borland International } { } {***************************************************} { This unit defines the interface to the DataEntry DDE server (DDEMLSRV.PAS). It defines the Service, Topic, and Item names supported by the Server, and also defines a data structure which may be used by the Client to hold the sampled data locally. The Data Entry Server makes its data samples available in text (cf_Text) form as three separate Topics. Clients may convert these into integer form for use with the data structure defined here. } unit Ddedlg; interface uses WinTypes, WinProcs, Classes, Graphics, Forms, Controls, Buttons, StdCtrls, Mask, ExtCtrls; type TDataEntry = class(TForm) OKBtn: TBitBtn; CancelBtn: TBitBtn; Bevel1: TBevel; Panel2: TPanel; Label1: TLabel; Label2: TLabel; Panel1: TPanel; Label4: TLabel; Panel3: TPanel; Label6: TLabel; Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; procedure OKBtnClick(Sender: TObject); procedure FormShow(Sender: TObject); private { Private declarations } public S1, S2, S3: String; { Public declarations } end; var DataEntry: TDataEntry; implementation {$R *.DFM} procedure TDataEntry.OKBtnClick(Sender: TObject); begin S1 := Edit1.Text; S2 := Edit2.Text; S3 := Edit3.Text; end; procedure TDataEntry.FormShow(Sender: TObject); begin Edit1.Text := '0'; Edit2.Text := '0'; Edit3.Text := '0'; Edit1.SetFocus; end; end. { *** END CODE FOR DDEDLG.PAS *** } Reference: 4/1/99 4:32:55 PM
Article originally contributed by Borland Staff