Platform SDK: Interprocess Communications

Establishing a Network DDE Conversation

Establishing a conversation between a client application and a server application on different computers is similar to establishing a conversation between a client and server on the same computer. The difference is that the client specifies a computer and a DDE share, rather than an application and a topic.

The first step is for the server to register with the DDEML by calling the DdeInitialize function. This call requires a pointer to the application-defined DDE callback function DdeCallback. The server also registers the service name that the DDE server supports by calling the DdeNameService function.

DWORD g_idInst;

BOOL MyDdeShareInit( LPTSTR lpszServer, PFNCALLBACK DdeCallback )
{
   HSZ hszService;
   char ServerBuf[MAX_COMPUTERNAME_LENGTH+8];

// Register the server application.
   if( DdeInitialize( 
      &g_idInst,
      (PFNCALLBACK) DdeCallback,
      APPCLASS_STANDARD | CBF_FAIL_SELFCONNECTIONS | 
      CBF_FAIL_REQUESTS | CBF_FAIL_EXECUTES,
      0L 
   ) != DMLERR_NO_ERROR )
      return FALSE;

// Check if "\\server" or just "server" is specified.
   if( lpszServer[0] == '\\' )
      wsprintf( ServerBuf, "%s\\NDDE$", lpszServer );
   else wsprintf( ServerBuf, "\\\\%s\\NDDE$", lpszServer );

// Register the service names.
   hszService = DdeCreateStringHandle( g_idInst, ServerBuf, 0 );
   DdeNameService(
      g_idInst,
      hszService,
      0,
      DNS_REGISTER
     );

   DdeFreeStringHandle( g_idInst, hszService );
   return TRUE;
}

The following example shows how you could call the MyConnect function to initialize DDEML for the server application on computer ServerA:

// Application-supplied callback function.
HDDEDATA CALLBACK DdeCallback( UINT iType, UINT iFmt, HCONV hConv, 
   HSZ hsz1, HSZ hsz2, HDDEDATA hData, DWORD dwData1, DWORD dwData2 )
{
   switch( iType )
   {
      case XTYP_CONNECT:
      // Validate topic for connection.
         ...
         return (HDDEDATA) TRUE;
      ...
      default:
         return (HDDEDATA) 0;
   }
}

MyDdeShareInit( "ServerA", DdeCallback );

As with any DDE conversation, the client and server applications must cooperate to establish a conversation. For network DDE, the client must have the computer name and the share name. The client then uses the DdeConnect function to establish a network DDE conversation.

HCONV g_hConv;

BOOL MyConnect( LPSTR lpszServer, LPTSTR lpszTopic )
{
   HSZ hszServer, hszTopic;
   char ServerBuf[MAX_COMPUTERNAME_LENGTH+8];

// Register the client application.
   if( DdeInitialize( 
      &g_idInst,
      (PFNCALLBACK) DdeCallback,
      APPCLASS_STANDARD | CBF_FAIL_SELFCONNECTIONS | 
      CBF_FAIL_REQUESTS | CBF_FAIL_EXECUTES,
      0L 
   ) != DMLERR_NO_ERROR )
      return FALSE;

// Check if "\\server" or just "server" is specified.
   if( lpszServer[0] == '\\' )
      wsprintf( ServerBuf, "%s\\NDDE$", lpszServer );
   else wsprintf( ServerBuf, "\\\\%s\\NDDE$", lpszServer );

   hszServer = DdeCreateStringHandle(g_idInst, ServerBuf, 0);
   hszTopic = DdeCreateStringHandle(g_idInst, lpszTopic, 0);

   if( (g_hConv = DdeConnect(g_idInst, 
      hszServer, 
      hszTopic, 
      NULL )
   ) == 0 )
      return FALSE;

   DdeFreeStringHandle( g_idInst, hszServer );
   DdeFreeStringHandle( g_idInst, hszTopic );
   return TRUE;
}

The following example shows how you could call this function to connect to the DDE share MyDdeShare$ on computer ServerA:

// Application-supplied callback function.
HDDEDATA CALLBACK DdeCallback( UINT iType, UINT iFmt, HCONV hConv, 
   HSZ hsz1, HSZ hsz2, HDDEDATA hData, DWORD dwData1, DWORD dwData2 )
{
   switch( iType )
   {
      ...
      default:
         return (HDDEDATA) 0;
   }
}

MyConnect( "ServerA", "MyDdeShare$" );