1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     DDE classes 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart and Markus Holzem 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  21     #pragma implementation "dde.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  38 #include "wx/module.h" 
  43 #include "wx/msw/private.h" 
  49 #if defined(__TWIN32__) || defined(__GNUWIN32_OLD__) 
  50     #include "wx/msw/gnuwin32/extra.h" 
  53 // some compilers headers don't define this one (mingw32) 
  54 #ifndef DMLERR_NO_ERROR 
  55     #define DMLERR_NO_ERROR (0) 
  57     // this one is also missing from some mingw32 headers, but there is no way 
  58     // to test for it (I know of) - the test for DMLERR_NO_ERROR works for me, 
  59     // but is surely not the right thing to do 
  61     HDDEDATA STDCALL 
DdeClientTransaction(LPBYTE pData
, 
  69 #endif // no DMLERR_NO_ERROR 
  71 // ---------------------------------------------------------------------------- 
  72 // macros and constants 
  73 // ---------------------------------------------------------------------------- 
  78     #define _EXPORT _export 
  82     #define DDE_CP      CP_WINUNICODE 
  84     #define DDE_CP      CP_WINANSI 
  87 #define GetHConv()       ((HCONV)m_hConv) 
  89 // default timeout for DDE operations (5sec) 
  90 #define DDE_TIMEOUT     5000 
  92 // ---------------------------------------------------------------------------- 
  94 // ---------------------------------------------------------------------------- 
  96 static wxDDEConnection 
*DDEFindConnection(HCONV hConv
); 
  97 static void DDEDeleteConnection(HCONV hConv
); 
  98 static wxDDEServer 
*DDEFindServer(const wxString
& s
); 
 100 extern "C" HDDEDATA EXPENTRY _EXPORT 
_DDECallback(WORD wType
, 
 109 // Add topic name to atom table before using in conversations 
 110 static HSZ 
DDEAddAtom(const wxString
& string
); 
 111 static HSZ 
DDEGetAtom(const wxString
& string
); 
 114 static HSZ 
DDEAtomFromString(const wxString
& s
); 
 115 static wxString 
DDEStringFromAtom(HSZ hsz
); 
 118 static wxString 
DDEGetErrorMsg(UINT error
); 
 119 static void DDELogError(const wxString
& s
, UINT error 
= DMLERR_NO_ERROR
); 
 121 // ---------------------------------------------------------------------------- 
 123 // ---------------------------------------------------------------------------- 
 125 static DWORD DDEIdInst 
= 0L; 
 126 static wxDDEConnection 
*DDECurrentlyConnecting 
= NULL
; 
 128 static wxList 
wxAtomTable(wxKEY_STRING
); 
 129 static wxList wxDDEClientObjects
; 
 130 static wxList wxDDEServerObjects
; 
 132 char *DDEDefaultIPCBuffer 
= NULL
; 
 133 int DDEDefaultIPCBufferSize 
= 0; 
 134 static bool DDEInitialized 
= FALSE
; 
 136 // ---------------------------------------------------------------------------- 
 138 // ---------------------------------------------------------------------------- 
 140 // A module to allow DDE cleanup without calling these functions 
 141 // from app.cpp or from the user's application. 
 143 class wxDDEModule 
: public wxModule
 
 147     bool OnInit() { return TRUE
; } 
 148     void OnExit() { wxDDECleanUp(); } 
 151     DECLARE_DYNAMIC_CLASS(wxDDEModule
) 
 154 // ---------------------------------------------------------------------------- 
 156 // ---------------------------------------------------------------------------- 
 158 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
) 
 159 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
) 
 160 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
) 
 161 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
) 
 163 // ============================================================================ 
 165 // ============================================================================ 
 167 // ---------------------------------------------------------------------------- 
 168 // initialization and cleanup 
 169 // ---------------------------------------------------------------------------- 
 171 extern void wxDDEInitialize() 
 173     if ( !DDEInitialized 
) 
 175         // Should insert filter flags 
 176         PFNCALLBACK callback 
= (PFNCALLBACK
) 
 177             MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance()); 
 178         UINT rc 
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L); 
 179         if ( rc 
!= DMLERR_NO_ERROR 
) 
 181             DDELogError(_T("Failed to initialize DDE"), rc
); 
 185             DDEInitialized 
= TRUE
; 
 192     if ( DDEIdInst 
!= 0 ) 
 194         DdeUninitialize(DDEIdInst
); 
 198     delete [] DDEDefaultIPCBuffer
; 
 201 // ---------------------------------------------------------------------------- 
 202 // functions working with the global connection list(s) 
 203 // ---------------------------------------------------------------------------- 
 205 // Global find connection 
 206 static wxDDEConnection 
*DDEFindConnection(HCONV hConv
) 
 208   wxNode 
*node 
= wxDDEServerObjects
.First(); 
 209   wxDDEConnection 
*found 
= NULL
; 
 210   while (node 
&& !found
) 
 212     wxDDEServer 
*object 
= (wxDDEServer 
*)node
->Data(); 
 213     found 
= object
->FindConnection((WXHCONV
) hConv
); 
 219   node 
= wxDDEClientObjects
.First(); 
 220   while (node 
&& !found
) 
 222     wxDDEClient 
*object 
= (wxDDEClient 
*)node
->Data(); 
 223     found 
= object
->FindConnection((WXHCONV
) hConv
); 
 229 // Global delete connection 
 230 static void DDEDeleteConnection(HCONV hConv
) 
 232   wxNode 
*node 
= wxDDEServerObjects
.First(); 
 234   while (node 
&& !found
) 
 236     wxDDEServer 
*object 
= (wxDDEServer 
*)node
->Data(); 
 237     found 
= object
->DeleteConnection((WXHCONV
) hConv
); 
 243   node 
= wxDDEClientObjects
.First(); 
 244   while (node 
&& !found
) 
 246     wxDDEClient 
*object 
= (wxDDEClient 
*)node
->Data(); 
 247     found 
= object
->DeleteConnection((WXHCONV
) hConv
); 
 252 // Find a server from a service name 
 253 static wxDDEServer 
*DDEFindServer(const wxString
& s
) 
 255   wxNode 
*node 
= wxDDEServerObjects
.First(); 
 256   wxDDEServer 
*found 
= NULL
; 
 257   while (node 
&& !found
) 
 259     wxDDEServer 
*object 
= (wxDDEServer 
*)node
->Data(); 
 261     if (object
->GetServiceName() == s
) 
 263     else node 
= node
->Next(); 
 268 // ---------------------------------------------------------------------------- 
 270 // ---------------------------------------------------------------------------- 
 272 wxDDEServer::wxDDEServer() 
 276     wxDDEServerObjects
.Append(this); 
 279 bool wxDDEServer::Create(const wxString
& server
) 
 281     m_serviceName 
= server
; 
 283     if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(server
), (HSZ
)NULL
, DNS_REGISTER
) ) 
 285         DDELogError(wxString::Format(_("Failed to register DDE server '%s'"), 
 294 wxDDEServer::~wxDDEServer() 
 296     if ( !!m_serviceName 
) 
 298         if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(m_serviceName
), 
 299                              (HSZ
)NULL
, DNS_UNREGISTER
) ) 
 301             DDELogError(wxString::Format(_("Failed to unregister DDE server '%s'"), 
 302                                          m_serviceName
.c_str())); 
 306     wxDDEServerObjects
.DeleteObject(this); 
 308     wxNode 
*node 
= m_connections
.First(); 
 311         wxDDEConnection 
*connection 
= (wxDDEConnection 
*)node
->Data(); 
 312         wxNode 
*next 
= node
->Next(); 
 313         connection
->OnDisconnect(); // May delete the node implicitly 
 317     // If any left after this, delete them 
 318     node 
= m_connections
.First(); 
 321         wxDDEConnection 
*connection 
= (wxDDEConnection 
*)node
->Data(); 
 322         wxNode 
*next 
= node
->Next(); 
 328 wxConnectionBase 
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */) 
 330     return new wxDDEConnection
; 
 333 wxDDEConnection 
*wxDDEServer::FindConnection(WXHCONV conv
) 
 335     wxNode 
*node 
= m_connections
.First(); 
 336     wxDDEConnection 
*found 
= NULL
; 
 337     while (node 
&& !found
) 
 339         wxDDEConnection 
*connection 
= (wxDDEConnection 
*)node
->Data(); 
 340         if (connection
->m_hConv 
== conv
) 
 342         else node 
= node
->Next(); 
 347 // Only delete the entry in the map, not the actual connection 
 348 bool wxDDEServer::DeleteConnection(WXHCONV conv
) 
 350     wxNode 
*node 
= m_connections
.First(); 
 352     while (node 
&& !found
) 
 354         wxDDEConnection 
*connection 
= (wxDDEConnection 
*)node
->Data(); 
 355         if (connection
->m_hConv 
== conv
) 
 360         else node 
= node
->Next(); 
 365 // ---------------------------------------------------------------------------- 
 367 // ---------------------------------------------------------------------------- 
 369 wxDDEClient::wxDDEClient() 
 373     wxDDEClientObjects
.Append(this); 
 376 wxDDEClient::~wxDDEClient() 
 378     wxDDEClientObjects
.DeleteObject(this); 
 379     wxNode 
*node 
= m_connections
.First(); 
 382         wxDDEConnection 
*connection 
= (wxDDEConnection 
*)node
->Data(); 
 383         delete connection
;  // Deletes the node implicitly (see ~wxDDEConnection) 
 384         node 
= m_connections
.First(); 
 388 bool wxDDEClient::ValidHost(const wxString
& /* host */) 
 393 wxConnectionBase 
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
), 
 394                                               const wxString
& server
, 
 395                                               const wxString
& topic
) 
 397     HCONV hConv 
= DdeConnect(DDEIdInst
, DDEAtomFromString(server
), DDEAtomFromString(topic
), 
 401         DDELogError(wxString::Format(_("Failed to create connection to server '%s' on topic '%s'"), 
 402                                      server
.c_str(), topic
.c_str())); 
 406         wxDDEConnection 
*connection 
= (wxDDEConnection
*) OnMakeConnection(); 
 409             connection
->m_hConv 
= (WXHCONV
) hConv
; 
 410             connection
->m_topicName 
= topic
; 
 411             connection
->m_client 
= this; 
 412             m_connections
.Append(connection
); 
 417     return (wxConnectionBase
*) NULL
; 
 420 wxConnectionBase 
*wxDDEClient::OnMakeConnection() 
 422     return new wxDDEConnection
; 
 425 wxDDEConnection 
*wxDDEClient::FindConnection(WXHCONV conv
) 
 427     wxNode 
*node 
= m_connections
.First(); 
 428     wxDDEConnection 
*found 
= NULL
; 
 429     while (node 
&& !found
) 
 431         wxDDEConnection 
*connection 
= (wxDDEConnection 
*)node
->Data(); 
 432         if (connection
->m_hConv 
== conv
) 
 434         else node 
= node
->Next(); 
 439 // Only delete the entry in the map, not the actual connection 
 440 bool wxDDEClient::DeleteConnection(WXHCONV conv
) 
 442     wxNode 
*node 
= m_connections
.First(); 
 444     while (node 
&& !found
) 
 446         wxDDEConnection 
*connection 
= (wxDDEConnection 
*)node
->Data(); 
 447         if (connection
->m_hConv 
== conv
) 
 452         else node 
= node
->Next(); 
 457 // ---------------------------------------------------------------------------- 
 459 // ---------------------------------------------------------------------------- 
 461 wxDDEConnection::wxDDEConnection(char *buffer
, int size
) 
 465         if (DDEDefaultIPCBuffer 
== NULL
) 
 466             DDEDefaultIPCBuffer 
= new char[DDEDefaultIPCBufferSize
]; 
 467         m_bufPtr 
= DDEDefaultIPCBuffer
; 
 468         m_bufSize 
= DDEDefaultIPCBufferSize
; 
 480     m_sendingData 
= NULL
; 
 483 wxDDEConnection::wxDDEConnection() 
 486     m_sendingData 
= NULL
; 
 489     if (DDEDefaultIPCBuffer 
== NULL
) 
 490         DDEDefaultIPCBuffer 
= new char[DDEDefaultIPCBufferSize
]; 
 492     m_bufPtr 
= DDEDefaultIPCBuffer
; 
 493     m_bufSize 
= DDEDefaultIPCBufferSize
; 
 496 wxDDEConnection::~wxDDEConnection() 
 499         m_server
->GetConnections().DeleteObject(this); 
 501         m_client
->GetConnections().DeleteObject(this); 
 504 // Calls that CLIENT can make 
 505 bool wxDDEConnection::Disconnect() 
 507     DDEDeleteConnection(GetHConv()); 
 509     bool ok 
= DdeDisconnect(GetHConv()) != 0; 
 512         DDELogError(_T("Failed to disconnect from DDE server gracefully")); 
 518 bool wxDDEConnection::Execute(const wxChar 
*data
, int size
, wxIPCFormat format
) 
 523         size 
= wxStrlen(data
) + 1; 
 526     bool ok 
= DdeClientTransaction((LPBYTE
)data
, size
, 
 536         DDELogError(_T("DDE execute request failed")); 
 542 char *wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
) 
 545     HSZ atom 
= DDEGetAtom(item
); 
 547     HDDEDATA returned_data 
= DdeClientTransaction(NULL
, 0, 
 553     if ( !returned_data 
) 
 555         DDELogError(_T("DDE data request failed")); 
 560     DWORD len 
= DdeGetData(returned_data
, (LPBYTE
)m_bufPtr
, m_bufSize
, 0); 
 562     DdeFreeDataHandle(returned_data
); 
 570 bool wxDDEConnection::Poke(const wxString
& item
, wxChar 
*data
, int size
, wxIPCFormat format
) 
 575         size 
= wxStrlen(data
) + 1; 
 578     HSZ item_atom 
= DDEGetAtom(item
); 
 579     bool ok 
= DdeClientTransaction((LPBYTE
)data
, size
, 
 587         DDELogError(_("DDE poke request failed")); 
 593 bool wxDDEConnection::StartAdvise(const wxString
& item
) 
 596     HSZ atom 
= DDEGetAtom(item
); 
 598     bool ok 
= DdeClientTransaction(NULL
, 0, 
 606         DDELogError(_("Failed to establish an advise loop with DDE server")); 
 612 bool wxDDEConnection::StopAdvise(const wxString
& item
) 
 615     HSZ atom 
= DDEGetAtom(item
); 
 617     bool ok 
= DdeClientTransaction(NULL
, 0, 
 625         DDELogError(_("Failed to terminate the advise loop with DDE server")); 
 631 // Calls that SERVER can make 
 632 bool wxDDEConnection::Advise(const wxString
& item
, 
 639         size 
= wxStrlen(data
) + 1; 
 642     HSZ item_atom 
= DDEGetAtom(item
); 
 643     HSZ topic_atom 
= DDEGetAtom(m_topicName
); 
 644     m_sendingData 
= data
; 
 648     bool ok 
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0; 
 651         DDELogError(_("Failed to send DDE advise notification")); 
 657 bool wxDDEConnection::OnDisconnect() 
 663 // ---------------------------------------------------------------------------- 
 665 // ---------------------------------------------------------------------------- 
 667 #define DDERETURN HDDEDATA 
 669 HDDEDATA EXPENTRY _EXPORT
 
 670 _DDECallback(WORD wType
, 
 676              DWORD 
WXUNUSED(lData1
), 
 677              DWORD 
WXUNUSED(lData2
)) 
 683                 wxString topic 
= DDEStringFromAtom(hsz1
), 
 684                          srv 
= DDEStringFromAtom(hsz2
); 
 685                 wxDDEServer 
*server 
= DDEFindServer(srv
); 
 688                     wxDDEConnection 
*connection 
= 
 689                         (wxDDEConnection
*) server
->OnAcceptConnection(topic
); 
 692                         connection
->m_server 
= server
; 
 693                         server
->GetConnections().Append(connection
); 
 694                         connection
->m_hConv 
= 0; 
 695                         connection
->m_topicName 
= topic
; 
 696                         DDECurrentlyConnecting 
= connection
; 
 697                         return (DDERETURN
)(DWORD
)TRUE
; 
 703         case XTYP_CONNECT_CONFIRM
: 
 705                 if (DDECurrentlyConnecting
) 
 707                     DDECurrentlyConnecting
->m_hConv 
= (WXHCONV
) hConv
; 
 708                     DDECurrentlyConnecting 
= NULL
; 
 709                     return (DDERETURN
)(DWORD
)TRUE
; 
 714         case XTYP_DISCONNECT
: 
 716                 wxDDEConnection 
*connection 
= DDEFindConnection(hConv
); 
 717                 if (connection 
&& connection
->OnDisconnect()) 
 719                     DDEDeleteConnection(hConv
);  // Delete mapping: hConv => connection 
 720                     return (DDERETURN
)(DWORD
)TRUE
; 
 727                 wxDDEConnection 
*connection 
= DDEFindConnection(hConv
); 
 731                     DWORD len 
= DdeGetData(hData
, 
 732                                            (LPBYTE
)connection
->m_bufPtr
, 
 733                                            connection
->m_bufSize
, 
 735                     DdeFreeDataHandle(hData
); 
 736                     if ( connection
->OnExecute(connection
->m_topicName
, 
 737                                                connection
->m_bufPtr
, 
 739                                                (wxIPCFormat
) wFmt
) ) 
 741                         return (DDERETURN
)(DWORD
)DDE_FACK
; 
 745                 return (DDERETURN
)DDE_FNOTPROCESSED
; 
 750                 wxDDEConnection 
*connection 
= DDEFindConnection(hConv
); 
 754                     wxString item_name 
= DDEStringFromAtom(hsz2
); 
 757                     char *data 
= connection
->OnRequest(connection
->m_topicName
, 
 764                             user_size 
= wxStrlen((wxChar
*)data
) + 1; 
 766                         HDDEDATA handle 
= DdeCreateDataHandle(DDEIdInst
, 
 773                         return (DDERETURN
)handle
; 
 781                 wxDDEConnection 
*connection 
= DDEFindConnection(hConv
); 
 785                     wxString item_name 
= DDEStringFromAtom(hsz2
); 
 787                     DWORD len 
= DdeGetData(hData
, 
 788                                            (LPBYTE
)connection
->m_bufPtr
, 
 789                                            connection
->m_bufSize
, 
 791                     DdeFreeDataHandle(hData
); 
 793                     connection
->OnPoke(connection
->m_topicName
, 
 795                                        (wxChar
*)connection
->m_bufPtr
, 
 799                     return (DDERETURN
)DDE_FACK
; 
 803                     return (DDERETURN
)DDE_FNOTPROCESSED
; 
 809                 wxDDEConnection 
*connection 
= DDEFindConnection(hConv
); 
 813                     wxString item_name 
= DDEStringFromAtom(hsz2
); 
 815                     return (DDERETURN
)connection
-> 
 816                                 OnStartAdvise(connection
->m_topicName
, item_name
); 
 824                 wxDDEConnection 
*connection 
= DDEFindConnection(hConv
); 
 828                     wxString item_name 
= DDEStringFromAtom(hsz2
); 
 830                     return (DDERETURN
)connection
-> 
 831                         OnStopAdvise(connection
->m_topicName
, item_name
); 
 839                 wxDDEConnection 
*connection 
= DDEFindConnection(hConv
); 
 841                 if (connection 
&& connection
->m_sendingData
) 
 843                     HDDEDATA data 
= DdeCreateDataHandle
 
 846                                         (LPBYTE
)connection
->m_sendingData
, 
 847                                         connection
->m_dataSize
, 
 850                                         connection
->m_dataType
, 
 854                     connection
->m_sendingData 
= NULL
; 
 856                     return (DDERETURN
)data
; 
 864                 wxDDEConnection 
*connection 
= DDEFindConnection(hConv
); 
 868                     wxString item_name 
= DDEStringFromAtom(hsz2
); 
 870                     DWORD len 
= DdeGetData(hData
, 
 871                                            (LPBYTE
)connection
->m_bufPtr
, 
 872                                            connection
->m_bufSize
, 
 874                     DdeFreeDataHandle(hData
); 
 875                     if ( connection
->OnAdvise(connection
->m_topicName
, 
 877                                               connection
->m_bufPtr
, 
 879                                               (wxIPCFormat
) wFmt
) ) 
 881                         return (DDERETURN
)(DWORD
)DDE_FACK
; 
 885                 return (DDERETURN
)DDE_FNOTPROCESSED
; 
 892 // ---------------------------------------------------------------------------- 
 893 // DDE strings and atoms 
 894 // ---------------------------------------------------------------------------- 
 897 static HSZ 
DDEAddAtom(const wxString
& string
) 
 899     HSZ atom 
= DDEAtomFromString(string
); 
 900     wxAtomTable
.Append(string
, (wxObject 
*)atom
); 
 904 static HSZ 
DDEGetAtom(const wxString
& string
) 
 906     wxNode 
*node 
= wxAtomTable
.Find(string
); 
 908         return (HSZ
)node
->Data(); 
 912         return (HSZ
)(wxAtomTable
.Find(string
)->Data()); 
 917 static HSZ 
DDEAtomFromString(const wxString
& s
) 
 919     wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") ); 
 921     HSZ hsz 
= DdeCreateStringHandle(DDEIdInst
, (wxChar
*) s
.c_str(), DDE_CP
); 
 924         DDELogError(_("Failed to create DDE string")); 
 930 static wxString 
DDEStringFromAtom(HSZ hsz
) 
 932     // all DDE strings are normally limited to 255 bytes 
 933     static const size_t len 
= 256; 
 936     (void)DdeQueryString(DDEIdInst
, hsz
, s
.GetWriteBuf(len
), len
, DDE_CP
); 
 942 // ---------------------------------------------------------------------------- 
 944 // ---------------------------------------------------------------------------- 
 946 static void DDELogError(const wxString
& s
, UINT error
) 
 950         error 
= DdeGetLastError(DDEIdInst
); 
 953     wxLogError(s 
+ _T(": ") + DDEGetErrorMsg(error
)); 
 956 static wxString 
DDEGetErrorMsg(UINT error
) 
 961         case DMLERR_NO_ERROR
: 
 962             err 
= _("no DDE error."); 
 965         case DMLERR_ADVACKTIMEOUT
: 
 966             err 
= _("a request for a synchronous advise transaction has timed out."); 
 969             err 
= _("the response to the transaction caused the DDE_FBUSY bit to be set."); 
 971         case DMLERR_DATAACKTIMEOUT
: 
 972             err 
= _("a request for a synchronous data transaction has timed out."); 
 974         case DMLERR_DLL_NOT_INITIALIZED
: 
 975             err 
= _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function."); 
 977         case DMLERR_DLL_USAGE
: 
 978             err 
= _("an application initialized as APPCLASS_MONITOR has\nattempted to perform a DDE transaction,\nor an application initialized as APPCMD_CLIENTONLY has \nattempted to perform server transactions."); 
 980         case DMLERR_EXECACKTIMEOUT
: 
 981             err 
= _("a request for a synchronous execute transaction has timed out."); 
 983         case DMLERR_INVALIDPARAMETER
: 
 984             err 
= _("a parameter failed to be validated by the DDEML."); 
 986         case DMLERR_LOW_MEMORY
: 
 987             err 
= _("a DDEML application has created a prolonged race condition."); 
 989         case DMLERR_MEMORY_ERROR
: 
 990             err 
= _("a memory allocation failed."); 
 992         case DMLERR_NO_CONV_ESTABLISHED
: 
 993             err 
= _("a client's attempt to establish a conversation has failed."); 
 995         case DMLERR_NOTPROCESSED
: 
 996             err 
= _("a transaction failed."); 
 998         case DMLERR_POKEACKTIMEOUT
: 
 999             err 
= _("a request for a synchronous poke transaction has timed out."); 
1001         case DMLERR_POSTMSG_FAILED
: 
1002             err 
= _("an internal call to the PostMessage function has failed. "); 
1004         case DMLERR_REENTRANCY
: 
1005             err 
= _("reentrancy problem."); 
1007         case DMLERR_SERVER_DIED
: 
1008             err 
= _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction."); 
1010         case DMLERR_SYS_ERROR
: 
1011             err 
= _("an internal error has occurred in the DDEML."); 
1013         case DMLERR_UNADVACKTIMEOUT
: 
1014             err 
= _("a request to end an advise transaction has timed out."); 
1016         case DMLERR_UNFOUND_QUEUE_ID
: 
1017             err 
= _("an invalid transaction identifier was passed to a DDEML function.\nOnce the application has returned from an XTYP_XACT_COMPLETE callback,\nthe transaction identifier for that callback is no longer valid."); 
1020             err
.Printf(_("Unknown DDE error %08x"), error
);