HOWTO: Write a DDEML Server for Excel and Use it with NetDDE

ID: Q238133


The information in this article applies to:


SUMMARY

This article walks you through building a DDE Management Library (DDEML) server application for Microsoft Excel using Visual C++, and illustrates how you can use the server remotely.


MORE INFORMATION

A common use of DDE (Dynamic Data Exchange) is to create an application that serves data to DDE clients such as Microsoft Excel. For instance, you might have an application that continuously gets real-time stock market quotes. Using DDE, you could expose this data from your application, and Excel users with access to it could reference it right in their calculations using DDE links.

The first part of this article walks you through building a very basic DDE server application using the DDEML. It updates a counter variable continuously, and advises any DDE clients listening as it changes:

  1. Create a new Visual C++ 6.0 project. Select "Win32 Application" as the project type, and name it "DdemlSvr." When it asks you what kind of project you want, select "An empty project."


  2. Add a new .cpp file to your project called "main.cpp."


  3. Paste the following code into main.cpp:


  4. 
    #include <windows.h>
    #include <stdio.h>
    #include <ddeml.h>
    
    // Globals...
    HSZ g_hszAppName;
    HSZ g_hszTopicName;
    HSZ g_hszItemName;
    int g_count = 0;
    DWORD g_idInst = 0;
    
    // Declarations:
    HDDEDATA EXPENTRY DdeCallback(UINT type, UINT fmt, HCONV hConv, HSZ hsz1, HSZ hsz2, HDDEDATA hData, DWORD dwData1, DWORD dwData2);
    
    // WinMain()..
    int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
       
       // Initialize DDEML...
       if(DdeInitialize(&g_idInst, DdeCallback, APPCLASS_STANDARD, 0)) {
          MessageBox(NULL, "DdeInitialize() failed", "Error", MB_SETFOREGROUND);
          return -1;
       }
       
       // Create string handles...
       g_hszAppName = DdeCreateStringHandle(g_idInst, "DdemlSvr", NULL);
       g_hszTopicName = DdeCreateStringHandle(g_idInst, "MyTopic", NULL);
       g_hszItemName = DdeCreateStringHandle(g_idInst, "MyItem", NULL);
       
       if( (g_hszAppName == 0) || (g_hszTopicName == 0) || (g_hszItemName == 0) ) {
          MessageBox(NULL, "DdeCreateStringHandle() failed", "Error", MB_SETFOREGROUND);
          return -2;
       }
       
       // Register DDE server
       if(!DdeNameService(g_idInst, g_hszAppName, NULL, DNS_REGISTER)) {
          MessageBox(NULL, "DdeNameService() failed!", "Error", MB_SETFOREGROUND);
          return -3;
       }
       
       // Create a timer to simulate changing data...
       SetTimer(0,0,1,0);
       
       // Message loop:
       MSG msg;
       while (GetMessage(&msg, NULL, 0, 0))  {
          // On WM_TIMER, change our counter, and update clients...
          if(msg.message == WM_TIMER) {
             g_count++;
             DdePostAdvise(g_idInst, g_hszTopicName, g_hszItemName);
          }
          TranslateMessage(&msg);
          DispatchMessage(&msg);
       }
       
       return msg.wParam;
    }
    
    
    // Our DDE Callback function...
    HDDEDATA EXPENTRY DdeCallback(UINT wType, UINT fmt, HCONV hConv, HSZ hsz1, HSZ hsz2, HDDEDATA hData, DWORD dwData1, DWORD dwData2) {
    
       switch (wType) {
          
       // ----------------------------------------------------------------
       case XTYP_CONNECT:
          // Client is trying to connect. Respond TRUE if we have what they want...
          if ((!DdeCmpStringHandles(hsz1, g_hszTopicName)) &&
             (!DdeCmpStringHandles(hsz2, g_hszAppName)))
             return (HDDEDATA)TRUE;   // SERVER supports Topic|Service
          else
             return FALSE;  // SERVER does not support Topic|Service
          
          // ----------------------------------------------------------------
       case XTYP_ADVSTART:
          // Client starting advisory loop.
          // Say "ok" if we have what they are asking for...   
    
          if((!DdeCmpStringHandles(hsz1, g_hszTopicName)) &&
             (!DdeCmpStringHandles(hsz2, g_hszItemName)))
             return (HDDEDATA)TRUE;   // SERVER supports Topic|Service
          else
             return FALSE;  // SERVER does not support Topic|Service
          // ----------------------------------------------------------------
                
       case XTYP_ADVREQ:
          
          // Client wants our data. Since this is specific to Excel, we'll
          // go ahead and assume they want XlTable-formatted data. For a
          // generic DDE server, you might want to handle various formats
          // specified by the passed in fmt parameter.
    
          if(!DdeCmpStringHandles(hsz1, g_hszTopicName) &&
             !DdeCmpStringHandles(hsz2, g_hszItemName)) {
             short xltableData[100];
             
             // tdtTable record...
             xltableData[0] = 0x0010; // tdtTable
             xltableData[1] = 4; // 2 short ints following
             xltableData[2] = 1; // # rows
             xltableData[3] = 1; // # cols
             
             // tdtInt record...
             xltableData[4] = 0x0006;
             xltableData[5] = 2;
             xltableData[6] = (short)g_count;
    
             return DdeCreateDataHandle(g_idInst, (UCHAR*)xltableData, 2*7, 0, g_hszItemName, fmt, 0);
          }    
          // ----------------------------------------------------------------
       default:
          return (HDDEDATA)NULL;
       }
    } 
  5. Compile and run the project.


The application first calls DdeInitialize() to initialize the DDEML library, passing the address of your DdeCallback function that acts in a manner similar to a window procedure. Then, it registers your application, topic, and item strings to get string handles. And finally, it registers your DDE server so clients can see it.

Once a client references you in a DDE link, your DdeCallback function will get called a few times with various parameters starting a DDE conversation. For this simple example, you just check to make sure they are asking for the right topic and name, and under the assumption the client is going to be Excel, just return your data in Excel's XlTable format.

Walk through the following steps to test your DDE server locally before continuing:
  1. Make sure your DdemlSvr.exe application is running. You can do this by examining the process list of the Windows Task Manager.


  2. Start Excel, and type the following in a cell: =DdemlSvr|MyTopic!MyItem


The cell should obtain the value of g_count from the DDE server application, and increment rapidly as it receives updates.

Now, you are ready to experiment using the server remotely using NetDDE. Follow these steps to set up the server:
  1. Start the DdemlSvr.exe application in a networked Windows NT 4.0 machine that will be your DDE Server: (Install it if it is not already running.)


  2. On that same machine, run "DdeShare" from the command-line.


  3. Choose Shares from the menu, and select DDE Shares.


  4. Click on Add a Share. Fill in the values under Application Name as follows:


  5. Share Name: MyShare$
    Old Style: DdemlSvr.DDE
    New Style: DdemlSvr.OLE
    Static: DdemlSvr

  6. Fill in MyTopic in the three boxes below Topic Name.


  7. Click OK, then select MyShare$, and click Trust Share.


  8. Check Initiate to Application Enable.


  9. Click OK on all the dialog boxes, and exit the DdeShare utility.


Follow these steps to set up the client and test the server:
  1. Start Excel on another Windows NT 4.0 machine on the same network; this will be your Client.


  2. Type the following in a cell, and substitute the name of your server machine on the network for SERVERNAME:


  3. ='\\SERVERNAME\NDDE$'|'MyShare$'!MyItem

    You should see results similar to those you saw when you tested on the client, a number incrementing continuously.


REFERENCES

For additional information, click the article numbers below to view the articles in the Microsoft Knowledge Base:

Q181946 HOWTO: Create a NetDDE Client and Server in Visual Basic
Q128491 Creating a NetDDE Link in Excel on Windows NT
Q75631 XLTABLE Clipboard Format Documentation Available
Additional information about DDE can be found in the MSDN articles entitled "Quick and Easy DDE Server" and "Client Sample: Client Use of DDEml Library."

© Microsoft Corporation 1999, All Rights Reserved.
Contributions by Joe Crump, Microsoft Corporation

Additional query words:


Keywords          : kbDDE kbExcel kbVC600 kbGrpDSO kbDSupport 
Version           : WINDOWS:2000,97; winnt:6.0
Platform          : WINDOWS winnt 
Issue type        : kbhowto 

Last Reviewed: August 5, 1999