Atlanta Custom Software Development 

 
   Search        Code/Page
 

User Login
Email

Password

 

Forgot the Password?
Services
» Web Development
» Maintenance
» Data Integration/BI
» Information Management
Programming
  Database
Automation
OS/Networking
Graphics
Links
Tools
» Regular Expr Tester
» Free Tools


Microsoft Windows provides several methods for transferring data between applications. One method is to use the Dynamic Data Exchange (DDE) protocol. The DDE protocol is a set of messages and guidelines. It sends messages between applications that share data and uses shared memory to exchange data between applications. Applications can use the DDE protocol for one-time data transfers and for continuous exchanges in which applications send updates to one another as new data becomes available.

About DDEML

The Dynamic Data Exchange Management Library (DDEML) provides an interface that simplifies the task of adding DDE capability to an application. Instead of sending, posting, and processing DDE messages directly, an application uses the functions provided by the DDEML to manage DDE conversations. A DDE conversation is the interaction between client and server applications. The DDEML also provides a means for managing the strings and data shared among DDE applications. Instead of using atoms and pointers to shared memory objects, DDE applications create and exchange string handles, which identify strings, and data handles, which identify DDE objects.

How it works ?

DDE always occurs between a client application and a server application. The DDE client application initiates the exchange by establishing a conversation with the server to send transactions to the server. A transaction is a request for data or services. The DDE server application responds to transactions by providing data or services to the client.

Some Terminology
  • Service Names : A service name is a string a server application responds to when a client attempts to establish a conversation with the server. A client must specify this service name to establish a conversation with the server. Although a server can respond to many service names, most servers respond to only one name.
  • Topic Names : A topic name is a string that identifies a logical data context. For servers that operate on file-based documents, topic names are typically filenames; for other servers, they are other application-specific strings. A client must specify a topic name along with a server's service name when it attempts to establish a conversation with a server.
  • Item Names : An item name is a string that identifies a unit of data a server can pass to a client during a transaction. For example, an item name might identify an integer, a string, several paragraphs of text, or a bitmap.
  • Transactions : The DDEML notifies an application about DDE activity that affects the application by sending transactions to the application's DDE callback function. A DDE transaction is similar to a message — it is a named constant accompanied by other parameters that contain additional information about the transaction.
  • DDE Callback Function : The DDEML passes a transaction to an application-defined DDE callback function that carries out an action appropriate to the type of transaction. For example, when a client application attempts to establish a conversation with a server application, the client calls the DdeConnect function. This function causes the DDEML to send an XTYP_CONNECT transaction to the server's DDE callback function. The callback function can allow the conversation by returning TRUE to the DDEML, or it can deny the conversation by returning FALSE.
  • System Topic :
The System topic provides a context for information of general interest to any DDE client. It is recommended that server applications support the System topic at all times.

Steps to implement DDE Server
  1. Initialize the DDE subsystem by calling DdeInitialize.
  2. Create string handles for server/topic using DdeCreateStringHandle
  3. Now to make sure that we are the only server with a specific server/topic name call DdeConnect with server/topic handles which we created in step 2. If this call succeed mean DDE server is already with specified server/topic name so dont execute the next step
  4. If step 3 is successful then call DdeNameService with DNS_REGISTER option to register the server with the DDE subsystem.
  5. If DDE Server is registered then you should receive various transaction request in DDECallback function. Start processing transactions in DDE Callback.
  6. When you dont want to run DDE Server then call DdeNameService with DNS_UNREGISTER option and then call DdeUninitialize to break down the link with the DDE subsystem so no more client requests can be processed.


DDE Transactions

Here is the list of some transactions and their description

XTYP_CONNECT
    A client uses the XTYP_CONNECT transaction to establish a conversation. A Dynamic Data Exchange (DDE) server callback function, DdeCallback, receives this transaction when a client specifies a service name that the server supports (and a topic name that is not NULL) in a call to the DdeConnect function.

XTYP_ADVDATA
    The XTYP_ADVDATA transaction informs the client that the value of the data item has changed. The Dynamic Data Exchange (DDE) client callback function, DdeCallback, receives this transaction after establishing an advise loop with a server.

XTYP_ADVREQ
    The XTYP_ADVREQ transaction informs the server that an advise transaction is outstanding on the specified topic name and item name pair and that data corresponding to the topic name and item name pair has changed. The system sends this transaction to the Dynamic Data Exchange (DDE) callback function, DdeCallback, after the server calls the DdePostAdvise function.

XTYP_ADVSTART
    A client uses the XTYP_ADVSTART transaction to establish an advise loop with a server. A Dynamic Data Exchange (DDE) server callback function, DdeCallback, receives this transaction when a client specifies XTYP_ADVSTART as the wType parameter of the DdeClientTransaction function.

XTYP_ADVSTOP

XTYP_CONNECT
    A client uses the XTYP_CONNECT transaction to establish a conversation. A Dynamic Data Exchange (DDE) server callback function, DdeCallback, receives this transaction when a client specifies a service name that the server supports (and a topic name that is not NULL) in a call to the DdeConnect function.

XTYP_CONNECT_CONFIRM
    A Dynamic Data Exchange (DDE) server callback function, DdeCallback, receives the XTYP_CONNECT_CONFIRM transaction to confirm that a conversation has been established with a client and to provide the server with the conversation handle. The system sends this transaction as a result of a previous XTYP_CONNECT or XTYP_WILDCONNECT transaction.

XTYP_DISCONNECT
    An application's Dynamic Data Exchange (DDE) callback function, DdeCallback, receives the XTYP_DISCONNECT transaction when the application's partner in a conversation uses the DdeDisconnect function to terminate the conversation.

XTYP_ERROR
    A Dynamic Data Exchange (DDE) callback function, DdeCallback, receives the XTYP_ERROR transaction when a critical error occurs.

XTYP_EXECUTE

XTYP_MONITOR
    A Dynamic Data Exchange (DDE) debugger's DDE callback function, DdeCallback, receives the XTYP_MONITOR transaction whenever a DDE event occurs in the system. To receive this transaction, an application must specify the APPCLASS_MONITOR value when it calls the DdeInitialize function.

XTYP_POKE

XTYP_REGISTER
    A Dynamic Data Exchange (DDE) callback function, DdeCallback, receives the XTYP_REGISTER transaction type whenever a Dynamic Data Exchange Management Library (DDEML) server application uses the DdeNameService function to register a service name, or whenever a non-DDEML application that supports the System topic is started.

XTYP_REQUEST

XTYP_UNREGISTER
    A Dynamic Data Exchange (DDE) callback function, DdeCallback, receives the XTYP_UNREGISTER transaction whenever a Dynamic Data Exchange Management Library (DDEML) server application uses the DdeNameService function to unregister a service name, or whenever a non-DDEML application that supports the System topic is terminated.

XTYP_WILDCONNECT
    The XTYP_WILDCONNECT transaction allows a client to establish a conversation on each of the server's service name and topic name pairs that match the specified service name and topic name. A Dynamic Data Exchange (DDE) server callback function, DdeCallback, receives this transaction when a client specifies a NULL service name, a NULL topic name, or both in a call to the DdeConnect or DdeConnectList function.

XTYP_XACT_COMPLETE 


Lets start with actual implementation of DDE Server. DDE Client Implementation is covered in the Part2 of this article

DDE Server

Step-By-Step Example
- Create a standard exe project
- Rename project to DDEServer
- Add one module to the project
- Add two command buttons and one textbox control on the form1
- Add the following code in form1

Click here to copy the following block
Option Explicit

Private hszDDEServer As Long ' String handle for the server name.
Private hszDDETopic As Long ' String handle for the topic name.
Private hszDDEItemPoke As Long  ' String handle for the Poke item name.
Private bRunning As Boolean ' Server running flag.

Private Sub Command2_Click()
  ' Clear the text box.
  Text1.Text = ""
End Sub

Private Sub Command1_Click()
  ' We have to initiate a DDEPostAdvise() in order to let all interested clients
  ' know that something has changed.
  If (DdePostAdvise(lInstID, 0, 0)) Then
    Debug.Print "DdePostAdvise() Success."
  Else
    Debug.Print "DdePostAdvise() Failed."
  End If
End Sub

Private Sub Form_Load()
  Command1.Caption = "Advice"
  Command2.Caption = "Clear"
  Me.Caption = "DDE Server"

  ' Initialize the DDE subsystem. We need to let the DDEML know what callback
  ' function we intend to use so we pass it address using the AddressOf operator.
  ' If we can't initialize the DDEML subsystem we exit.
  If DdeInitialize(lInstID, AddressOf DDECallback, APPCMD_FILTERINITS, 0) Then
    Exit Sub
  End If

  ' Now that the DDEML subsystem is initialized we create string handles for our
  ' server/topic name.
  hszDDEServer = DdeCreateStringHandle(lInstID, DDE_SERVER, CP_WINANSI)
  hszDDETopic = DdeCreateStringHandle(lInstID, DDE_TOPIC, CP_WINANSI)
  hszDDEItemPoke = DdeCreateStringHandle(lInstID, DDE_POKE, CP_WINANSI)
  hszDDEItemAdvise = DdeCreateStringHandle(lInstID, DDE_ADVISE, CP_WINANSI)

  ' Lets check to see if another DDE server has already registered with identical
  ' server/topic names. If so we'll exit. If we were to continue the DDE subsystem
  ' could become unstable when a client tried to converse with the server/topic.
  If (DdeConnect(lInstID, hszDDEServer, hszDDETopic, ByVal 0&)) Then
    MsgBox "A DDE server named " & Chr(34) & DDE_SERVER & Chr(34) & " with topic " & _
        Chr(34) & DDE_TOPIC & Chr(34) & " is already running.", vbExclamation, App.Title
    Unload Me
    Exit Sub
  End If

  ' We need to register the server with the DDE subsystem.
  If (DdeNameService(lInstID, hszDDEServer, 0, DNS_REGISTER)) Then
    ' Set the server running flag.
    bRunning = True
  End If

  Me.WindowState = vbMinimized
End Sub

Private Sub Form_Terminate()
  ' We need to release our string handles.
  DdeFreeStringHandle lInstID, hszDDEServer
  DdeFreeStringHandle lInstID, hszDDETopic
  DdeFreeStringHandle lInstID, hszDDEItemPoke
  DdeFreeStringHandle lInstID, hszDDEItemAdvise

  ' Unregister the DDE server.
  If bRunning Then
    DdeNameService lInstID, hszDDEServer, 0, DNS_UNREGISTER
  End If

  ' Break down the link with the DDE subsystem.
  DdeUninitialize lInstID
End Sub

- Add the following code in module1

Click here to copy the following block
Option Explicit

' The DDE Instance ID and Item variables need to be global since there used by
' both the form and the code module.
Global lInstID As Long    ' DDE instance identifier.
Global hszDDEItemAdvise As Long  ' String handle for the item name.

'*************************************************************************
' DDEML Server Constants
'*************************************************************************
Public Const DDE_SERVER = "MyServer"
Public Const DDE_TOPIC = "MyTopic"
Public Const DDE_ADVISE = "MyAdvise"
Public Const DDE_REQUEST = "MyRequest"
Public Const DDE_POKE = "MyPoke"
Public Const DDE_COMMAND1 = "MAX"
Public Const DDE_COMMAND2 = "MIN"
Public Const DDE_COMMAND3 = "NORMAL"

' For some reason when performing an advise the data seems to be truncated by three
' characters. So to alleviate that problem we use a magic number constant.
Public Const MAGIC_NUMBER = 3

' This is just a string that we'll return whenever a client performs a DDE
' request if the input window on the form is empty. Otherwise we'll return
' whatever is in the window.
Public Const DDE_REQUEST_STRING = "The server input window is empty."

'*************************************************************************
' DDEML Return Values
'*************************************************************************
Public Const DMLERR_NO_ERROR = 0
Public Const DMLERR_ADVACKTIMEOUT = &H4000
Public Const DMLERR_BUSY = &H4001
Public Const DMLERR_DATAACKTIMEOUT = &H4002
Public Const DMLERR_DLL_NOT_INITIALIZED = &H4003
Public Const DMLERR_DLL_USAGE = &H4004
Public Const DMLERR_EXECACKTIMEOUT = &H4005
Public Const DMLERR_INVALIDPARAMETER = &H4006
Public Const DMLERR_LOW_MEMORY = &H4007
Public Const DMLERR_MEMORY_ERROR = &H4008
Public Const DMLERR_NOTPROCESSED = &H4009
Public Const DMLERR_NO_CONV_ESTABLISHED = &H400A
Public Const DMLERR_POKEACKTIMEOUT = &H400B
Public Const DMLERR_POSTMSG_FAILED = &H400C
Public Const DMLERR_REENTRANCY = &H400D
Public Const DMLERR_SERVER_DIED = &H400E
Public Const DMLERR_SYS_ERROR = &H400F
Public Const DMLERR_UNADVACKTIMEOUT = &H4010
Public Const DMLERR_UNFOUND_QUEUE_ID = &H4011

'*************************************************************************
' DDEML Flags
'*************************************************************************
Public Const APPCMD_FILTERINITS = &H20&
Public Const XCLASS_BOOL = &H1000
Public Const XCLASS_DATA = &H2000
Public Const XCLASS_FLAGS = &H4000
Public Const XCLASS_NOTIFICATION = &H8000
Public Const XTYPF_NOBLOCK = &H2  ' CBR_BLOCK doesn't seem to work
Public Const XTYP_ADVDATA = (&H10 Or XCLASS_FLAGS)
Public Const XTYP_ADVREQ = (&H20 Or XCLASS_DATA Or XTYPF_NOBLOCK)
Public Const XTYP_ADVSTART = (XCLASS_BOOL Or &H30)
Public Const XTYP_ADVSTOP = (XCLASS_NOTIFICATION Or &H40)
Public Const XTYP_CONNECT = (XCLASS_BOOL Or &H60 Or XTYPF_NOBLOCK)
Public Const XTYP_CONNECT_CONFIRM = (XCLASS_NOTIFICATION Or &H70 Or XTYPF_NOBLOCK)
Public Const XTYP_DISCONNECT = (XCLASS_NOTIFICATION Or &HC0 Or XTYPF_NOBLOCK)
Public Const XTYP_ERROR = (XCLASS_NOTIFICATION Or &H0 Or XTYPF_NOBLOCK)
Public Const XTYP_EXECUTE = (XCLASS_FLAGS Or &H50)
Public Const XTYP_MASK = &HF0
Public Const XTYP_MONITOR = (XCLASS_NOTIFICATION Or &HF0 Or XTYPF_NOBLOCK)
Public Const XTYP_POKE = (XCLASS_FLAGS Or &H90)
Public Const XTYP_REGISTER = (XCLASS_NOTIFICATION Or &HA0 Or XTYPF_NOBLOCK)
Public Const XTYP_REQUEST = (XCLASS_DATA Or &HB0)
Public Const XTYP_SHIFT = 4 ' shift to turn XTYP_ into an index
Public Const XTYP_UNREGISTER = (XCLASS_NOTIFICATION Or &HD0 Or XTYPF_NOBLOCK)
Public Const XTYP_WILDCONNECT = (XCLASS_DATA Or &HE0 Or XTYPF_NOBLOCK)
Public Const XTYP_XACT_COMPLETE = (XCLASS_NOTIFICATION Or &H80)
Public Const CP_WINANSI = 1004  ' Default codepage for DDE conversations.
Public Const CP_WINUNICODE = 1200
Public Const CF_TEXT = 1
Public Const CBF_SKIP_ALLNOTIFICATIONS = &H3C0000
Public Const TIMEOUT_ASYNC = &HFFFF
Public Const DNS_REGISTER = &H1
Public Const DNS_UNREGISTER = &H2
Public Const DDE_FACK = &H8000
Public Const DDE_FBUSY = &H4000
Public Const DDE_FNOTPROCESSED = &H0
Public Const HDATA_APPOWNED = &H1

'*************************************************************************
' DDEML Type Declarations
'*************************************************************************
Public Type SECURITY_QUALITY_OF_SERVICE
  Length As Long
  Impersonationlevel As Integer
  ContextTrackingMode As Integer
  EffectiveOnly As Long
End Type

Public Type CONVCONTEXT
  cb As Long
  wFlags As Long
  wCountryID As Long
  iCodePage As Long
  dwLangID As Long
  dwSecurity As Long
  qos As SECURITY_QUALITY_OF_SERVICE
End Type

'*************************************************************************
' DDEML Function Declarations
'*************************************************************************
Public Declare Function DdeInitialize Lib "user32" Alias "DdeInitializeA" _
    (pidInst As Long, _
    ByVal pfnCallback As Long, _
    ByVal afCmd As Long, _
    ByVal ulRes As Long) As Integer

' Removed the alias.
Public Declare Function DdeUninitialize Lib "user32" _
    (ByVal idInst As Long) As Long

Public Declare Function DdeNameService Lib "user32" _
    (ByVal idInst As Long, _
    ByVal hsz1 As Long, _
    ByVal hsz2 As Long, _
    ByVal afCmd As Long) As Long

' Removed the alias.
Public Declare Function DdeConnect Lib "user32" _
    (ByVal idInst As Long, _
    ByVal hszService As Long, _
    ByVal hszTopic As Long, _
    pCC As Any) As Long

' Removed the alias.
Public Declare Function DdeDisconnect Lib "user32" _
    (ByVal hConv As Long) As Long

Public Declare Function DdeCreateStringHandle Lib "user32" _
    Alias "DdeCreateStringHandleA" _
    (ByVal idInst As Long, _
    ByVal psz As String, _
    ByVal iCodePage As Long) As Long

' Removed the alias.
Public Declare Function DdeFreeStringHandle Lib "user32" _
    (ByVal idInst As Long, _
    ByVal hsz As Long) As Long

' Removed the alias.
Public Declare Function DdeQueryString Lib "user32" _
    Alias "DdeQueryStringA" _
    (ByVal idInst As Long, _
    ByVal hsz As Long, _
    ByVal psz As String, _
    ByVal cchMax As Long, _
    ByVal iCodePage As Long) As Long

Public Declare Function DdeCreateDataHandle Lib "user32" _
    (ByVal idInst As Long, _
    ByVal pSrc As String, _
    ByVal cb As Long, _
    ByVal cbOff As Long, _
    ByVal hszItem As Long, _
    ByVal wFmt As Long, _
    ByVal afCmd As Long) As Long

' Removed the alias and changed the first parameter from "ByRef pData as Byte"
' to "ByVal pData as String".
Public Declare Function DdeClientTransaction Lib "user32" _
    (ByVal pData As String, _
    ByVal cbData As Long, _
    ByVal hConv As Long, _
    ByVal hszItem As Long, _
    ByVal wFmt As Long, _
    ByVal wType As Long, _
    ByVal dwTimeout As Long, _
    pdwResult As Long) As Long

' The API loader provides an alias of "DdeGetDataA" for this function.
' You need to remove it because the DLL entry point can't be found for
' the alias.
Public Declare Function DdeGetData Lib "user32" _
    (ByVal hData As Long, _
    ByVal pDst As String, _
    ByVal cbMax As Long, _
    ByVal cbOff As Long) As Long

' Removed the alias.
Public Declare Function DdeFreeDataHandle Lib "user32" _
    (ByVal hData As Long) As Long

' Removed the alias.
Public Declare Function DdeGetLastError Lib "user32" _
    (ByVal idInst As Long) As Long

Public Declare Function DdePostAdvise Lib "user32" _
    (ByVal idInst As Long, _
    ByVal hszTopic As Long, _
    ByVal hszItem As Long) As Long

Function DDECallback(ByVal uType As Long, _
           ByVal uFmt As Long, _
           ByVal hConv As Long, _
           ByVal hszString1 As Long, _
           ByVal hszString2 As Long, _
           ByVal hData As Long, _
           ByVal dwData1 As Long, _
           ByVal dwData2 As Long) As Long

  Dim lSize As Long
  Dim lRet As Long
  Dim sBuffer As String
  Dim sRequest As String

  Select Case uType
      ' Process the connect transaction.
    Case XTYP_CONNECT
      ' Just return a positive acknowledgement. If we don't the conversation will
      ' never be completed between us and the client.
      lRet = DDE_FACK

      Form1.WindowState = vbNormal

      ' Process the request transaction.
    Case XTYP_REQUEST
      ' What's the size of the string?
      lSize = DdeQueryString(lInstID, hszString2, vbNullString, 0, CP_WINANSI)

      ' Allocate space for the string.
      sBuffer = Space(lSize)

      ' Grab the string.
      DdeQueryString lInstID, hszString2, sBuffer, lSize + 1, CP_WINANSI

      ' Check to see if the client is requesting something we can supply.
      If (sBuffer = DDE_REQUEST) Then
        If (Form1.Text1.Text = "") Then
          sRequest = DDE_REQUEST_STRING
        Else
          sRequest = Form1.Text1.Text
        End If

        ' Create a data object and populate it with our string. Then return the object.
        lRet = DdeCreateDataHandle(lInstID, sRequest, Len(sRequest), 0, hszString2, CF_TEXT, 0)
      Else
        ' The client didn't ask nicely so we're not going to process the request.
        lRet = DDE_FNOTPROCESSED
      End If

      ' Process the execute transaction.
    Case XTYP_EXECUTE
      ' What's the size of the string?
      lSize = DdeGetData(ByVal hData, vbNullString, 0, 0)

      ' Allocate space for the buffer.
      sBuffer = Space(lSize)

      ' Grab the DDE data object.
      DdeGetData ByVal hData, sBuffer, lSize, 0

      sBuffer = UCase(sBuffer)

      ' Set the default return.
      lRet = DDE_FACK

      ' Did the client specify a command that we understand?
      If (sBuffer = DDE_COMMAND1) Then
        Form1.WindowState = vbMaximized
      ElseIf (sBuffer = DDE_COMMAND2) Then
        Form1.WindowState = vbMinimized
      ElseIf (sBuffer = DDE_COMMAND3) Then
        Form1.WindowState = vbNormal
      Else
        lRet = DDE_FNOTPROCESSED
      End If

      ' Process the poke request.
    Case XTYP_POKE
      lSize = DdeQueryString(lInstID, hszString2, vbNullString, 0, CP_WINANSI)
      sBuffer = Space(lSize)
      DdeQueryString lInstID, hszString2, sBuffer, lSize + 1, CP_WINANSI

      If (sBuffer = DDE_POKE) Then
        ' Since the client is sending data for an item that we support we can
        ' grab the data.
        lSize = DdeGetData(ByVal hData, vbNullString, 0, 0)

        ' Allocate space for the buffer.
        sBuffer = Space(lSize)

        ' Grab the DDE data object.
        DdeGetData ByVal hData, sBuffer, lSize, 0

        ' Make sure the window is visible.
        Form1.WindowState = vbNormal

        ' Add the data to the text box.
        Form1.Text1.Text = sBuffer

        lRet = DDE_FACK
      Else
        lRet = DDE_FNOTPROCESSED
      End If

    Case XTYP_ADVREQ
      Debug.Print "We got the advise request. Conv: " & hConv

      If (Form1.Text1.Text = "") Then
        sRequest = DDE_REQUEST_STRING
      Else
        sRequest = Form1.Text1.Text
      End If

      ' Now create the data handle for the changed data.
      lRet = DdeCreateDataHandle(lInstID, sRequest, Len(sRequest) + MAGIC_NUMBER, 0, hszDDEItemAdvise, CF_TEXT, 0)

    Case XTYP_ADVSTART
      Debug.Print "Start advise request made. Conv: " & hConv

      ' Enable the "Advise" button.
      Form1.Command1.Enabled = True

      lRet = DDE_FACK

    Case XTYP_ADVSTOP
      Debug.Print "Stop advise request made. Conv: " & hConv

      ' Disable the "Advise" button.
      Form1.Command1.Enabled = False

      lRet = DDE_FACK

  End Select

  ' Set the final callback return.
  DDECallback = lRet

End Function

- Now compile the project and run the exe. When you run the application it will minimize the window and it will become active on any client request.

Now next part is implementing DDE Client.

Next : Interprocess communication using DDE (Dynamic Data Exchange) - Part2


Submitted By : Nayan Patel  (Member Since : 5/26/2004 12:23:06 PM)

Job Description : He is the moderator of this site and currently working as an independent consultant. He works with VB.net/ASP.net, SQL Server and other MS technologies. He is MCSD.net, MCDBA and MCSE. In his free time he likes to watch funny movies and doing oil painting.
View all (893) submissions by this author  (Birth Date : 7/14/1981 )


Home   |  Comment   |  Contact Us   |  Privacy Policy   |  Terms & Conditions   |  BlogsZappySys

© 2008 BinaryWorld LLC. All rights reserved.