Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Windows DDE for Real-Time Applications


JAN93: WINDOWS DDE FOR REAL-TIME APPLICATIONS

WINDOWS DDE FOR REAL-TIME APPLICATIONS

Dynamic data exchange extends the Windows message protocol

Kamal Shah

Kamal Shah holds an MSEE from the University of Texas at Austin and is a software engineer for Intel. He can be contacted at 5200 N E Elam Young Parkway, Hillsboro, OR 97124-6497; 800-438-4769.


Windows dynamic data exchange (DDE) provides a powerful mechanism for communicating among Windows applications. This communication takes place as applications send messages to each other to initiate conversations, to request and share data, and to terminate conversations.

This article examines how real-time applications can communicate with Windows applications using Windows DDE protocol. I'll first examine the DDE mechanism in the realm of Windows applications, then describe how this capability is extended to include real-time applications.

Getting Up to Speed with DDE

DDE is one of several mechanisms of interprocess communication supported by Windows. It is an extension of the messaging scheme around which Windows is designed. Of the two applications involved in the data exchange, one is known as the server and another as the client. The client application is the consumer of the data, the server is the provider. It's also possible for an application to act as both a client and a server.

The client program starts the conversation by posting a message that includes the server's name (the application's name, such as a name of a database program) and a name of a topic of its interest. If the server acknowledges this request positively, a link is established between the two. The client can then request data from the server. The server provides the data if it has access to the data requested; otherwise it replies negatively. The client can also poke (write) data items to the server. The conversation stops when the client and the server send termination messages to each other.

DDE Links

There are three types of links supported by DDE in Windows: cold, hot, and warm. As Figure 1 illustrates, a cold-link conversation begins when a client sends a WM_DDE_INITIATE message identifying the application and the topic it is interested in. A server application that supports the requested topic responds to the client with a WM_DDE_ACK (acknowledge) message. The client then requests a particular data item by sending a WM_DDE_REQUEST message. The server, if able to provide the requested data, responds with WM_DDE_DATA message to the client. If the server does not have access to requested data, it posts a negative WM_DDE_ACK message to the client. On receipt of the requested data, the client can also optionally post a WM_DDE_ACK message to the server. The conversation is terminated when the client and the server post each other WM_DDE_TERMINATE messages.

The hot link (see Figure 2) allows the client to get updated data from the server without explicit requests. The conversation begins in the same way as in the cold link. Next, the client informs the server to send updated data by sending a WM_DDE_ADVISE message. The server replies positively or negatively, depending upon its access to the requested data. At this point, the server is obliged to inform the client whenever the value of the data item changes by posting WM_DDE_DATA messages to the client. When the client is no longer interested in the particular data item, it sends WM_DDE_UNADVISE message to the server. The conversation is terminated when the client and the server post each other WM_DDE_TERMINATE messages.

As shown in Figure 3, the warm link falls somewhere in between the cold link and the hot link. In this case, the client wants to be notified of changes in the data item without immediately receiving the value of the new data item. The conversation begins as usual. Next, the client sends a WM_DDE_ADVISE message to the server with a special flag indicating deferred update of the data item value. When the data item changes, the server posts a WM_DDE_DATA message with NULL data value. To get the actual data value, the client posts a WM_DDE_REQUEST message to the server, just as in the cold link. When the client is no longer interested in the particular data item, it sends WM_DDE_UNADVISE message to the server. The conversation is terminated when the client and the server post each other WM_DDE_TERMINATE messages.

iRMX for Windows DDE Architecture

iRMX for Windows is a real-time, multitasking operating system that provides preemptive, priority-based, interrupt-driven scheduling. iRMX for Windows co-exists with DOS or Microsoft Windows on 386 or higher microprocessor-based PCs, and users can run DOS/Windows applications and iRMX applications simultaneously on a single system.

As Figure 4 illustrates, iRMX DDE consists of two components: a DDE library used by iRMX applications and a NetBIOS-to-DDE Router application used by Windows applications. This software is designed such that iRMX and Windows applications running on the same machine, or running on different machines connected by a network, can communicate with each other transparently. The NetBIOS-to-DDE Router is a Windows application. NetBIOS is a peer-to-peer session-layer LAN protocol that provides the primitives to conduct process-to-process communications across the network nodes. As long as two systems are running the same NetBIOS implementations at the transport layer, the machines will be able to communicate.

The NetBIOS-to-DDE Router acts as a surrogate for the iRMX application for DDE communication. As far as the Windows application is concerned, the iRMX application is just another Windows application. The DDE library communicates with the DDE Router using standard network interfaces. The DDE addressing scheme, which includes an application name and a topic name, is extended to include a third identifier, a machine name. This machine name is made available by including it in the win.ini Windows configuration file. This machine name, coupled with an application name and a topic name, uniquely identifies a Windows application to the iRMX application. When an iRMX application wants to initiate a DDE conversation with a Windows application, it uses this machine name in addition to the application and the topic names.

Similarly, when a Windows application wants to initiate a DDE conversation with an iRMX application, it uses an identifier of the form machine_name-%application_name\topic_name. In this case, machine_name is the name under which an iRMX application registers itself. In essence, we have expanded the application name to include the machine name. These two names are separated by a user-configurable separator such as %.

When the NetBIOS-to-DDE Router receives a WM_DDE_INITIATE message, it verifies that the application-name string is of the form machine_name_ %app_name. Once this is confirmed, the NetBIOS-to-DDE Router sends the DDE messages via NetBIOS using the NCBs (Network Control Blocks, a format that NetBIOS understands). The NetBIOS driver for iRMX for Windows picks up these NCBs, converts them into transport-protocol information blocks, and ships them to iRMX networking. The iRMX networking passes these blocks to the iRMX DDE library, which delivers them to the requesting iRMX application in an appropriate format.

Similarly, an iRMX application's DDE message first gets converted to transport-protocol information blocks by the iRMX DDE library. These blocks are sent to the NetBIOS driver. The NetBIOS driver sends these blocks to the NetBIOS-to-DDE Router using the NCBs. Using the information in the NCBs, the Router forms DDE messages and passes them on to the appropriate Windows application.

Sample Session

A simple example illustrates the concepts involved in DDE programming. In the example, the iRMX application is constructed as a DDE server (see Listing One, page 94) and the Windows application is developed as a DDE client (see Listing Two, page 94). The iRMX application has access to the share prices of four different companies on a real-time basis. The Windows client application displays these instantaneous price changes on the screen. Listing Three (page 98) is the DDE client header file, Listing Four (page 98) is the DDE client-definition file, and Listing Five (page 98) is the Windows client-generation batch file.

First, the iRMX application registers itself as a DDE server by providing a machine name and addresses of its two callback functions. Then it waits for the client application to establish a hot-link connection for the specific data items.

The Windows client initiates the conversation with the server by sending the server's machine name, an application name, and a topic name in the WM_DDE_INITIATE message. Once the conversation is established, the client sends a WM_DDE_ADVISE message to the server to receive instantaneous updates of share prices. In the example program, the real-time changes in the prices are simulated by sending periodic updates in increments of 25 cents. The server sends this updates using the server_dde_update_link DDE library call.

Conclusions

iRMX for Windows has extended the Windows DDE protocol to include real-time applications, thus enabling rich graphical user interfaces for a real-time back end. Implemented on an industry-standard interface such as NetBIOS, the iRMX DDE capability allows networks of PCs running Windows programs to communicate and exchange information with real-time applications.



_WINDOWS DDE FOR REAL-TIME APPLICATIONS_
by Kamal Shah


[LISTING ONE]
<a name="003c_000a">

/* iRMX for Windows DDE server */

#include        <stdio.h>
#include        <rmxc.h>
#include        <string.h>
#include        <rmxdde.h>

#define TRUE 0xFFFF
#define FALSE 0

typedef struct
     {
     char *szCompanyName;
     float stockprice;
     }
    STOCKPRICE;
    STOCKPRICE shareprice [] =
     {
     "Intel", 50.0,
     "Microsoft", 70.0,
     "IBM", 90.0,
     "Apple",50.0
     };
#define NUM_COMPANIES  (sizeof(shareprice) /sizeof(shareprice[0]))
WORD hlink_status[NUM_COMPANIES] = {FALSE,FALSE,FALSE,FALSE};
WORD         server_conv_id;
WORD         hot_link_on = FALSE;
WORD         link_closed = TRUE;
WORD         status;
WORD         i;
WORD         link_cnt = 0;

/*----------------------------------------------------------
   err_check: error processing function
------------------------------------------------------------*/
void err_check (WORD status, const char *err_msg)
     {
     if (status != DDE_OK)
          {
          fprintf (stderr, "\n---%s  Status = 0x%x\n", err_msg, status);
          exit (1);
          }
     }
/* Server Functions */
/*---------------------------------------------------------------------
    conv_callback: This is the conversation callback
    function that is registered at the server_dde_register call.
-----------------------------------------------------------------------*/
WORD
conv_callback (char *client_name_p, char *service_name_p, char *topic_name_p,
                                     WORD conversation_id, WORD function_code)
     {
     server_conv_id = conversation_id;
    if (function_code == DDE_INITIATE)
       link_closed = FALSE;
    else if (function_code == DDE_TERMINATE)
       link_closed = TRUE;
          return 0;
          }
/*-------------------------------------------------------------------------
  data_callback: This is the data callback function that is registered at the
  server_dde_register call. It processes DDE requests from the client.
--------------------------------------------------------------------------*/
WORD
data_callback (char *client_name_p, char *service_name_p, char *topic_name_p,
                        WORD conversation_id, char *item_p, char *data_buf_p,
                        WORD data_buf_size, WORD function_code)
{
     if (function_code == DDE_HOT_LINK)
     {

   if(!strcmp(shareprice[link_cnt].szCompanyName,item_p))
   hlink_status[link_cnt++] = TRUE;
   return 0;
}
     else if (function_code == DDE_CLOSE_LINK)
     {
   for (i = 0; i < NUM_COMPANIES; i++)
   {
   if(!strcmp(shareprice[i].szCompanyName,item_p))
   hlink_status[i] = FALSE;
   return 0;
   }
        }
}
main(int argc, char *argv[])
     {
     WORD           dde_status;
     WORD           conv_id;
     char           price_buf[10];
     printf("Initializing dde library\n");
     dde_library_init (NULL, &dde_status);
     err_check (dde_status, "dde_library_init() failed");
     printf("\n---Registering self as DDE server"
               "\n    machine name     = %s"
               "\n    application name = %s\n", "servernode", "rmxapp");
     server_dde_register ("servernode",
                        "rmxapp",
                         conv_callback,
                         data_callback,
                         &dde_status);
   err_check (dde_status, "server_dde_register() failed");
   while (link_closed == TRUE)
        {
        rqsleep(300,&status);
        printf("Waiting for  conv. to be established\n");
        }
   while (link_closed == FALSE)
        {
        rqsleep(300,&status);
        for (i = 0; i < NUM_COMPANIES; i++)
         {
          if (hlink_status[i] == TRUE)
            {
            shareprice[i].stockprice = shareprice[i].stockprice +.25;
            sprintf(price_buf, "%f", shareprice[i].stockprice);
            printf("%s\n",shareprice[i].szCompanyName);
            server_dde_update_link(server_conv_id,
                                 shareprice[i].szCompanyName,
                                 price_buf,
                                 &dde_status);
            err_check (dde_status, "server_dde_update_link() failed");
            }

         }
        }
        printf("data xfer over\n");
        exit(0);
}




<a name="003c_000b">
<a name="003c_000c">
[LISTING TWO]
<a name="003c_000c">



//**********************************************************
//  DDE client application
//**********************************************************

#include <windows.h>
#include <dde.h>
#include <stdlib.h>

#include <string.h>
#include "hlcli.h"

    // Global variables

char szAppName [] = "ddecli";
HANDLE    hInst ;
short   cxChar, cyChar;
BOOL    bFirstTime = TRUE;

STOCKPRICE shareprice [] =
    {
    "Intel", 50.0,
    "Microsoft", 70.0,
    "IBM", 90.0,
    "Apple",50.0
    };
#define NUM_COMPANIES  (sizeof(shareprice)/sizeof(shareprice[0]))

//**********************************************************
//   FUNCTION : WinMain(HANDLE, HANDLE, LPSTR, int)
//   PURPOSE  : Create the main application window
//**********************************************************
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
              LPSTR lpszCmdLine, int nCmdShow)
{
    HWND  hwnd ;
    MSG   msg ;
    WNDCLASS   wcDdeclient ;
     // Register the window class if this is the first instance
    if (!hPrevInstance)
    {
     wcDdeclient.style = CS_HREDRAW | CS_VREDRAW ;
     wcDdeclient.lpfnWndProc = WndProc ;
     wcDdeclient.cbClsExtra = 0 ;
     wcDdeclient.cbWndExtra = 0 ;
     wcDdeclient.hInstance = hInstance ;
     wcDdeclient.hIcon = LoadIcon (NULL,IDI_APPLICATION);
     wcDdeclient.hCursor = LoadCursor (NULL, IDC_ARROW) ;
     wcDdeclient.hbrBackground = GetStockObject (WHITE_BRUSH) ;

     wcDdeclient.lpszMenuName = NULL;
     wcDdeclient.lpszClassName = szAppName ;

     RegisterClass (&wcDdeclient);
    }
     // Create the main window
    hwnd = CreateWindow (szAppName, "DDE Client Application",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          NULL,
                          NULL,
                          hInstance,
                          NULL) ;
    hInst = hInstance ;
     // Show the main window and send ourselves a paint message
    ShowWindow (hwnd, nCmdShow) ;
    UpdateWindow (hwnd) ;

     // Send ourselves a message that will cause a conversation
     // to be started with the server
    SendMessage (hwnd, WM_INITDDE, 0, 0L) ;

     // Enter the message loop
    while (GetMessage (&msg, NULL, 0, 0))
    {
     TranslateMessage (&msg) ;
     DispatchMessage (&msg) ;
    }

    return msg.wParam ;
}
//**********************************************************
//   FUNCTION : WndProc (HWND, WORD, WORD, LONG)
//   PURPOSE  : Processes all messages
//**********************************************************
long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
{
    static HWND     hwndServer = NULL ;
    static BOOL     fInit = TRUE ;
    static char     szServName [] = "servernode%rmxapp",
                   szTopicName [] = "stockprice" ;
    ATOM          aAppName, aTopicName, aItemName ;
    char          szBuf [24], szStockprice [16], szItemName [16] ;
    DDEACK             DdeAck ;
    DDEDATA FAR     *lpDdeData ;
    DDEADVISE FAR   *lpDdeAdvise ;
    DWORD         dwTime ;
    GLOBALHANDLE    hDdeData,hDdeAdvise ;
    HDC           hDC ;
    MSG           msg ;
    TEXTMETRIC     tm ;
    PAINTSTRUCT    ps ;
    short           i ;
    WORD       wStatus;
    float   stockprice;

    switch (message)
    {
       case WM_CREATE:
          // Get a handle to a device context
         hDC = GetDC (hwnd) ;

          // Get the metrics of the system font
         GetTextMetrics (hDC, &tm) ;

          // Save the character width and height
         cxChar = tm.tmAveCharWidth ;
         cyChar = tm.tmHeight + tm.tmExternalLeading ;

          // Release the device context handle
         ReleaseDC (hwnd, hDC) ;

         return (FALSE) ;
     case WM_INITDDE:
          // To start a DDE conversation, first: Create glogal atomsto send
          // with the WM_DDE_INITIATE message
         aAppName = GlobalAddAtom (szServName) ;
         aTopicName = GlobalAddAtom (szTopicName) ;

          // Now broadcast WM_DDE_INITIATE message, the value of -1 in the
          // first parameter means the message is sent to all windows.
         SendMessage (0xFFFF, WM_DDE_INITIATE, hwnd,
                                             MAKELONG (aAppName, aTopicName)) ;
          // SendMessage is a synchronous call, so we would receive a response
          // in WM_DDE_ACK message before the SendMessage call returns.
          // Attempt to load Ddeserver if we get no response.
         if (hwndServer == NULL)
         {
          WinExec (szServName, SW_SHOWMINNOACTIVE) ;
          SendMessage (0xFFFF, WM_DDE_INITIATE, hwnd,
                                            MAKELONG (aAppName, aTopicName)) ;
         }
          // Delete the atoms we created above and reset the flag for
          // WM_DDE_ACK processing
         GlobalDeleteAtom (aAppName) ;
         GlobalDeleteAtom (aTopicName) ;
                   fInit = FALSE ;
          // Check again for a response. If we haven't received one,
          // display a message box and exit this routine.
         if (hwndServer == NULL)
         {
          MessageBox (hwnd, "Cannot communicate with iRMX SERVER!",
                   szAppName, MB_ICONSTOP | MB_OK) ;
          return (FALSE) ;
         }
      // Post WM_DDE_ADVISE messages
      for (i = 0; i < NUM_COMPANIES; i++)
       {
        hDdeAdvise = GlobalAlloc (GHND | GMEM_DDESHARE, sizeof (DDEADVISE));
        lpDdeAdvise = (DDEADVISE FAR *) GlobalLock (hDdeAdvise);
        lpDdeAdvise->fAckReq = TRUE;
        lpDdeAdvise->fDeferUpd = FALSE;
        lpDdeAdvise->cfFormat = CF_TEXT;

        GlobalUnlock (hDdeAdvise);

        aItemName = GlobalAddAtom(shareprice[i].szCompanyName);
        if (!PostMessage (hwndServer,WM_DDE_ADVISE,hwnd,
                        MAKELONG(hDdeAdvise,aItemName)))
         {
         GlobalFree (hDdeAdvise);
         GlobalDeleteAtom (aItemName);
         break ;
         }
      DdeAck.fAck = FALSE;
      dwTime = GetCurrentTime ();
      while (GetCurrentTime () - dwTime < TIMEOUT)
      {
      if (PeekMessage (&msg, hwnd, WM_DDE_ACK,WM_DDE_ACK, PM_REMOVE))
        {
          GlobalDeleteAtom (HIWORD (msg.lParam));
          DdeAck = * (DDEACK *) & LOWORD (msg.lParam);
          if (DdeAck.fAck == FALSE )
          GlobalFree (hDdeAdvise);
          break ;
        }
      }
     if (DdeAck.fAck == FALSE)
      break ;
     while (PeekMessage (&msg, hwnd, WM_DDE_FIRST,WM_DDE_LAST, PM_REMOVE))
        {
        DispatchMessage (&msg);
        }
     }
     if (i < NUM_COMPANIES)
       {
        MessageBox (hwnd, "WM_DDE_ADVISE Failed!",szAppName,
                   MB_ICONEXCLAMATION | MB_OK);
       }
  return (FALSE);
     case WM_DDE_ACK:
          // We are only responding here when we are in the middle of a
          // WM_DDE_INITIATE send routine as determined by the fInit flag.
          // We save the server window handle and delete the global atoms.
         if (fInit)
         {
          hwndServer = wParam ;
          GlobalDeleteAtom (LOWORD (lParam)) ;
          GlobalDeleteAtom (HIWORD (lParam)) ;
         }
         return (FALSE) ;
     case WM_DDE_DATA:
          // wParam is the handle to the sending window LOWORD (lParam) is the
          // handle to the DDEDATA memory. HIWORD (lParam) is the item atom.
         hDdeData = LOWORD (lParam) ;
         lpDdeData = (DDEDATA FAR *) GlobalLock (hDdeData);
         aItemName = HIWORD (lParam) ;
        DdeAck.bAppReturnCode = 0;
        DdeAck.reserved       = 0;
        DdeAck.fBusy          = FALSE;
        DdeAck.fAck           = FALSE;

          // Check for matching format and data item
         if (lpDdeData->cfFormat == CF_TEXT)
         {
          GlobalGetAtomName (aItemName, szItemName, sizeof (szItemName)) ;
    for (i = 0; i < NUM_COMPANIES; i++)
              if (strcmp (szItemName, shareprice[i].szCompanyName) == 0)
                 break;
              if (i < NUM_COMPANIES)
         {
                lstrcpy (szStockprice, lpDdeData->Value) ;
                shareprice[i].stockprice = atof(szStockprice) ;
                InvalidateRect (hwnd, NULL, FALSE) ;
                DdeAck.fAck = TRUE ;
              }
         }
       if (lpDdeData-> fAckReq == TRUE)
       {
         wStatus = * (WORD *) & DdeAck ;
         if (!PostMessage (wParam, WM_DDE_ACK, hwnd,
                          MAKELONG (wStatus,aItemName)))
            {
            GlobalDeleteAtom (aItemName) ;
            GlobalUnlock (hDdeData);
            GlobalFree  (hDdeData );
         return 0;
            }
      }
      else
       {
        GlobalDeleteAtom (aItemName) ;
       }
       //clean up
       if (lpDdeData->fRelease == TRUE || DdeAck.fAck == FALSE)
       {
       GlobalUnlock (hDdeData);
       GlobalFree (hDdeData);
       }
       else
       {
       GlobalUnlock (hDdeData);
       }
       return 0;
     case WM_PAINT:
        if (bFirstTime)
        {
          bFirstTime = FALSE;
          hDC = BeginPaint (hwnd, &ps) ;
          TextOut(hDC, cxChar, cyChar,BANNER,strlen(BANNER));
           EndPaint (hwnd, &ps) ;
           return (FALSE) ;
        }
        else
        {
          hDC = BeginPaint (hwnd, &ps) ;
          for (i = 0; i < NUM_COMPANIES; i++)
          {
             sprintf(szBuf, "%lf",shareprice[i].stockprice);
             TextOut(hDC,cxChar*(i*20), cyChar*2,szBuf,strlen(szBuf));
          }
           EndPaint (hwnd, &ps) ;

           return (FALSE) ;
        }
     case WM_DDE_TERMINATE:
          // Post a WM_DDE_TERMINATE message back and null our server handle
         PostMessage (hwndServer, WM_DDE_TERMINATE, hwnd, 0L) ;
         hwndServer = NULL ;
         return (FALSE) ;
     case WM_CLOSE:
         if (hwndServer == NULL)
          break ;
        // Post WM_DDE_UNADVISE message
        PostMessage (hwndServer, WM_DDE_UNADVISE,hwnd,MAKELONG (CF_TEXT,NULL));
       // Wait for server to acknowledge
       dwTime = GetCurrentTime () ;
         while (GetCurrentTime () - dwTime < TIMEOUT)
         {
          if (PeekMessage (&msg, hwnd, WM_DDE_ACK, WM_DDE_ACK, PM_REMOVE))
              break ;
         }
          // Tell server to terminate
         PostMessage (hwndServer, WM_DDE_TERMINATE, hwnd, 0L) ;
          // Wait for server to acknowledge
         dwTime = GetCurrentTime () ;
         while (GetCurrentTime () - dwTime < TIMEOUT)
         {
          if (PeekMessage (&msg, hwnd, WM_DDE_TERMINATE, WM_DDE_TERMINATE,
                                                                   PM_REMOVE))
              break ;
         }
         break ;
     case WM_DESTROY:
         PostQuitMessage (0) ;
         return (FALSE) ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;

}




<a name="003c_000d">
<a name="003c_000e">
[LISTING THREE]
<a name="003c_000e">
// DDE Client Header File -- Define a structure type for  stock prices


typedef struct
     {
     char *szCompanyName ;
     float  stockprice;
     }
     STOCKPRICE ;


    // Define user defined messages

#define WM_INITDDE      (WM_USER + 1)
#define TIMEOUT         1000
#define BANNER "Intel   Microsoft   IBM     Apple"
    // Function declarations
int PASCAL WinMain (HANDLE, HANDLE, LPSTR, int) ;
long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;





<a name="003c_000f">
<a name="003c_0010">
[LISTING FOUR]
<a name="003c_0010">

; DDE Client Definition File -- Definition file for the link editor

NAME        ddeclient
DESCRIPTION    'DDE Client application'
; Program executable type
EXETYPE        WINDOWS
; Stub is a small program that will be displayed if this
; program is run from the DOS prompt
STUB        'WINSTUB.EXE'

CODE        PRELOAD MOVABLE
DATA        PRELOAD MOVABLE MULTIPLE

HEAPSIZE       1024
STACKSIZE      8192

; All Windows callback functions must be named here
EXPORTS
     WndProc  @1





<a name="003c_0011">
<a name="003c_0012">
[LISTING FIVE]
<a name="003c_0012">

cl -c -Gw -Zp %1.c
link /NOD /NOE /align:16 %1, %1.exe,,libw+slibcew,%1.def
rc %1.exe













Copyright © 1993, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.