1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: DDE classes
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "dde.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
38 #include "wx/module.h"
41 #include "wx/hashmap.h"
44 #include "wx/msw/private.h"
49 // ----------------------------------------------------------------------------
50 // macros and constants
51 // ----------------------------------------------------------------------------
56 #define _EXPORT _export
60 #define DDE_CP CP_WINUNICODE
62 #define DDE_CP CP_WINANSI
65 #define GetHConv() ((HCONV)m_hConv)
67 // default timeout for DDE operations (5sec)
68 #define DDE_TIMEOUT 5000
70 // ----------------------------------------------------------------------------
72 // ----------------------------------------------------------------------------
74 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
75 static void DDEDeleteConnection(HCONV hConv
);
76 static wxDDEServer
*DDEFindServer(const wxString
& s
);
78 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(WORD wType
,
87 // Add topic name to atom table before using in conversations
88 static HSZ
DDEAddAtom(const wxString
& string
);
89 static HSZ
DDEGetAtom(const wxString
& string
);
92 static HSZ
DDEAtomFromString(const wxString
& s
);
93 static wxString
DDEStringFromAtom(HSZ hsz
);
94 static void DDEFreeString(HSZ hsz
);
97 static wxString
DDEGetErrorMsg(UINT error
);
98 static void DDELogError(const wxString
& s
, UINT error
= DMLERR_NO_ERROR
);
100 // ----------------------------------------------------------------------------
102 // ----------------------------------------------------------------------------
104 WX_DECLARE_STRING_HASH_MAP( HSZ
, wxAtomMap
);
106 static DWORD DDEIdInst
= 0L;
107 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
108 static wxAtomMap wxAtomTable
;
110 #include "wx/listimpl.cpp"
112 WX_DEFINE_LIST(wxDDEClientList
);
113 WX_DEFINE_LIST(wxDDEServerList
);
114 WX_DEFINE_LIST(wxDDEConnectionList
);
116 static wxDDEClientList wxDDEClientObjects
;
117 static wxDDEServerList wxDDEServerObjects
;
119 static bool DDEInitialized
= false;
121 // ----------------------------------------------------------------------------
123 // ----------------------------------------------------------------------------
125 // A module to allow DDE cleanup without calling these functions
126 // from app.cpp or from the user's application.
128 class wxDDEModule
: public wxModule
132 bool OnInit() { return true; }
133 void OnExit() { wxDDECleanUp(); }
136 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
139 // ----------------------------------------------------------------------------
141 // ----------------------------------------------------------------------------
143 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
144 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
145 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
146 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
148 // ============================================================================
150 // ============================================================================
152 // ----------------------------------------------------------------------------
153 // initialization and cleanup
154 // ----------------------------------------------------------------------------
156 extern void wxDDEInitialize()
158 if ( !DDEInitialized
)
160 // Should insert filter flags
161 PFNCALLBACK callback
= (PFNCALLBACK
)
162 MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance());
163 UINT rc
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L);
164 if ( rc
!= DMLERR_NO_ERROR
)
166 DDELogError(_T("Failed to initialize DDE"), rc
);
170 DDEInitialized
= true;
177 // deleting them later won't work as DDE won't be initialized any more
178 wxASSERT_MSG( wxDDEServerObjects
.empty() &&
179 wxDDEClientObjects
.empty(),
180 _T("all DDE objects should be deleted by now") );
184 if ( DDEIdInst
!= 0 )
186 DdeUninitialize(DDEIdInst
);
191 // ----------------------------------------------------------------------------
192 // functions working with the global connection list(s)
193 // ----------------------------------------------------------------------------
195 // Global find connection
196 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
198 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
199 wxDDEConnection
*found
= NULL
;
200 while (serverNode
&& !found
)
202 wxDDEServer
*object
= serverNode
->GetData();
203 found
= object
->FindConnection((WXHCONV
) hConv
);
204 serverNode
= serverNode
->GetNext();
212 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
213 while (clientNode
&& !found
)
215 wxDDEClient
*object
= clientNode
->GetData();
216 found
= object
->FindConnection((WXHCONV
) hConv
);
217 clientNode
= clientNode
->GetNext();
222 // Global delete connection
223 static void DDEDeleteConnection(HCONV hConv
)
225 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
227 while (serverNode
&& !found
)
229 wxDDEServer
*object
= serverNode
->GetData();
230 found
= object
->DeleteConnection((WXHCONV
) hConv
);
231 serverNode
= serverNode
->GetNext();
238 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
239 while (clientNode
&& !found
)
241 wxDDEClient
*object
= clientNode
->GetData();
242 found
= object
->DeleteConnection((WXHCONV
) hConv
);
243 clientNode
= clientNode
->GetNext();
247 // Find a server from a service name
248 static wxDDEServer
*DDEFindServer(const wxString
& s
)
250 wxDDEServerList::compatibility_iterator node
= wxDDEServerObjects
.GetFirst();
251 wxDDEServer
*found
= NULL
;
252 while (node
&& !found
)
254 wxDDEServer
*object
= node
->GetData();
256 if (object
->GetServiceName() == s
)
262 node
= node
->GetNext();
269 // ----------------------------------------------------------------------------
271 // ----------------------------------------------------------------------------
273 wxDDEServer::wxDDEServer()
277 wxDDEServerObjects
.Append(this);
280 bool wxDDEServer::Create(const wxString
& server
)
282 m_serviceName
= server
;
284 HSZ hsz
= DDEAtomFromString(server
);
292 bool success
= (DdeNameService(DDEIdInst
, hsz
, (HSZ
) NULL
, DNS_REGISTER
)
297 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
306 wxDDEServer::~wxDDEServer()
308 if ( !m_serviceName
.IsEmpty() )
310 HSZ hsz
= DDEAtomFromString(m_serviceName
);
314 if ( !DdeNameService(DDEIdInst
, hsz
,
315 (HSZ
) NULL
, DNS_UNREGISTER
) )
317 DDELogError(wxString::Format(
318 _("Failed to unregister DDE server '%s'"),
319 m_serviceName
.c_str()));
326 wxDDEServerObjects
.DeleteObject(this);
328 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
331 wxDDEConnection
*connection
= node
->GetData();
332 wxDDEConnectionList::compatibility_iterator next
= node
->GetNext();
333 connection
->SetConnected(false);
334 connection
->OnDisconnect(); // May delete the node implicitly
338 // If any left after this, delete them
339 node
= m_connections
.GetFirst();
342 wxDDEConnection
*connection
= node
->GetData();
343 wxDDEConnectionList::compatibility_iterator next
= node
->GetNext();
349 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
351 return new wxDDEConnection
;
354 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
356 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
357 wxDDEConnection
*found
= NULL
;
358 while (node
&& !found
)
360 wxDDEConnection
*connection
= node
->GetData();
361 if (connection
->m_hConv
== conv
)
363 else node
= node
->GetNext();
368 // Only delete the entry in the map, not the actual connection
369 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
371 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
374 wxDDEConnection
*connection
= node
->GetData();
375 if (connection
->m_hConv
== conv
)
377 m_connections
.Erase(node
);
382 node
= node
->GetNext();
388 // ----------------------------------------------------------------------------
390 // ----------------------------------------------------------------------------
392 wxDDEClient::wxDDEClient()
396 wxDDEClientObjects
.Append(this);
399 wxDDEClient::~wxDDEClient()
401 wxDDEClientObjects
.DeleteObject(this);
402 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
405 wxDDEConnection
*connection
= node
->GetData();
406 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
407 node
= m_connections
.GetFirst();
411 bool wxDDEClient::ValidHost(const wxString
& /* host */)
416 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
),
417 const wxString
& server
,
418 const wxString
& topic
)
420 HSZ hszServer
= DDEAtomFromString(server
);
424 return (wxConnectionBase
*) NULL
;
428 HSZ hszTopic
= DDEAtomFromString(topic
);
432 DDEFreeString(hszServer
);
433 return (wxConnectionBase
*) NULL
;
437 HCONV hConv
= ::DdeConnect(DDEIdInst
, hszServer
, hszTopic
,
438 (PCONVCONTEXT
) NULL
);
440 DDEFreeString(hszServer
);
441 DDEFreeString(hszTopic
);
446 DDELogError( wxString::Format(
447 _("Failed to create connection to server '%s' on topic '%s'"),
448 server
.c_str(), topic
.c_str()) );
452 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
455 connection
->m_hConv
= (WXHCONV
) hConv
;
456 connection
->m_topicName
= topic
;
457 connection
->m_client
= this;
458 m_connections
.Append(connection
);
463 return (wxConnectionBase
*) NULL
;
466 wxConnectionBase
*wxDDEClient::OnMakeConnection()
468 return new wxDDEConnection
;
471 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
473 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
474 wxDDEConnection
*found
= NULL
;
475 while (node
&& !found
)
477 wxDDEConnection
*connection
= node
->GetData();
478 if (connection
->m_hConv
== conv
)
480 else node
= node
->GetNext();
485 // Only delete the entry in the map, not the actual connection
486 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
488 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
491 wxDDEConnection
*connection
= node
->GetData();
492 if (connection
->m_hConv
== conv
)
494 m_connections
.Erase(node
);
497 else node
= node
->GetNext();
502 // ----------------------------------------------------------------------------
504 // ----------------------------------------------------------------------------
506 wxDDEConnection::wxDDEConnection(wxChar
*buffer
, int size
)
507 : wxConnectionBase(buffer
, size
)
513 m_sendingData
= NULL
;
516 wxDDEConnection::wxDDEConnection()
520 m_sendingData
= NULL
;
525 wxDDEConnection::~wxDDEConnection()
529 m_server
->GetConnections().DeleteObject(this);
531 m_client
->GetConnections().DeleteObject(this);
534 // Calls that CLIENT can make
535 bool wxDDEConnection::Disconnect()
537 if ( !GetConnected() )
540 DDEDeleteConnection(GetHConv());
542 bool ok
= DdeDisconnect(GetHConv()) != 0;
545 DDELogError(_T("Failed to disconnect from DDE server gracefully"));
548 SetConnected( false ); // so we don't try and disconnect again
553 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
558 size
= wxStrlen(data
) + 1;
561 bool ok
= DdeClientTransaction((LPBYTE
)data
,
562 size
* sizeof(wxChar
),
572 DDELogError(_T("DDE execute request failed"));
578 wxChar
*wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
582 HSZ atom
= DDEGetAtom(item
);
584 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
590 if ( !returned_data
)
592 DDELogError(_T("DDE data request failed"));
597 DWORD len
= DdeGetData(returned_data
, NULL
, 0, 0);
598 len
= (DWORD
)ceil( double(len
)/sizeof(wxChar
) );
600 wxChar
*data
= GetBufferAtLeast( len
);
601 wxASSERT_MSG(data
!= NULL
,
602 _T("Buffer too small in wxDDEConnection::Request") );
603 (void) DdeGetData(returned_data
, (LPBYTE
)data
, len
, 0);
605 (void) DdeFreeDataHandle(returned_data
);
613 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
618 size
= wxStrlen(data
) + 1;
621 HSZ item_atom
= DDEGetAtom(item
);
622 bool ok
= DdeClientTransaction((LPBYTE
)data
,
623 size
* sizeof(wxChar
),
631 DDELogError(_("DDE poke request failed"));
637 bool wxDDEConnection::StartAdvise(const wxString
& item
)
640 HSZ atom
= DDEGetAtom(item
);
642 bool ok
= DdeClientTransaction(NULL
, 0,
650 DDELogError(_("Failed to establish an advise loop with DDE server"));
656 bool wxDDEConnection::StopAdvise(const wxString
& item
)
659 HSZ atom
= DDEGetAtom(item
);
661 bool ok
= DdeClientTransaction(NULL
, 0,
669 DDELogError(_("Failed to terminate the advise loop with DDE server"));
675 // Calls that SERVER can make
676 bool wxDDEConnection::Advise(const wxString
& item
,
683 size
= wxStrlen(data
) + 1;
686 HSZ item_atom
= DDEGetAtom(item
);
687 HSZ topic_atom
= DDEGetAtom(m_topicName
);
688 m_sendingData
= data
; // mrf: potential for scope problems here?
692 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
695 DDELogError(_("Failed to send DDE advise notification"));
701 bool wxDDEConnection::OnDisconnect()
707 // ----------------------------------------------------------------------------
709 // ----------------------------------------------------------------------------
711 #define DDERETURN HDDEDATA
713 HDDEDATA EXPENTRY _EXPORT
714 _DDECallback(WORD wType
,
720 DWORD
WXUNUSED(lData1
),
721 DWORD
WXUNUSED(lData2
))
727 wxString topic
= DDEStringFromAtom(hsz1
),
728 srv
= DDEStringFromAtom(hsz2
);
729 wxDDEServer
*server
= DDEFindServer(srv
);
732 wxDDEConnection
*connection
=
733 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
736 connection
->m_server
= server
;
737 server
->GetConnections().Append(connection
);
738 connection
->m_hConv
= 0;
739 connection
->m_topicName
= topic
;
740 DDECurrentlyConnecting
= connection
;
741 return (DDERETURN
)(DWORD
)true;
747 case XTYP_CONNECT_CONFIRM
:
749 if (DDECurrentlyConnecting
)
751 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
752 DDECurrentlyConnecting
= NULL
;
753 return (DDERETURN
)(DWORD
)true;
758 case XTYP_DISCONNECT
:
760 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
763 connection
->SetConnected( false );
764 if (connection
->OnDisconnect())
766 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
767 return (DDERETURN
)(DWORD
)true;
775 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
779 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
780 len
= (DWORD
)ceil( double(len
)/sizeof(wxChar
) );
782 wxChar
*data
= connection
->GetBufferAtLeast( len
);
783 wxASSERT_MSG(data
!= NULL
,
784 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
786 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
788 DdeFreeDataHandle(hData
);
790 if ( connection
->OnExecute(connection
->m_topicName
,
793 (wxIPCFormat
) wFmt
) )
795 return (DDERETURN
)(DWORD
)DDE_FACK
;
799 return (DDERETURN
)DDE_FNOTPROCESSED
;
804 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
808 wxString item_name
= DDEStringFromAtom(hsz2
);
811 wxChar
*data
= connection
->OnRequest(connection
->m_topicName
,
818 user_size
= wxStrlen((wxChar
*)data
) + 1;
820 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
822 user_size
*sizeof(wxChar
),
827 return (DDERETURN
)handle
;
835 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
839 wxString item_name
= DDEStringFromAtom(hsz2
);
841 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
842 len
= (DWORD
)ceil( double(len
) / sizeof(wxChar
) );
844 wxChar
*data
= connection
->GetBufferAtLeast( len
);
845 wxASSERT_MSG(data
!= NULL
,
846 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
848 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
850 DdeFreeDataHandle(hData
);
852 connection
->OnPoke(connection
->m_topicName
,
858 return (DDERETURN
)DDE_FACK
;
862 return (DDERETURN
)DDE_FNOTPROCESSED
;
868 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
872 wxString item_name
= DDEStringFromAtom(hsz2
);
874 return (DDERETURN
)connection
->
875 OnStartAdvise(connection
->m_topicName
, item_name
);
883 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
887 wxString item_name
= DDEStringFromAtom(hsz2
);
889 return (DDERETURN
)connection
->
890 OnStopAdvise(connection
->m_topicName
, item_name
);
898 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
900 if (connection
&& connection
->m_sendingData
)
902 HDDEDATA data
= DdeCreateDataHandle
905 (LPBYTE
)connection
->m_sendingData
,
906 connection
->m_dataSize
*sizeof(wxChar
),
909 connection
->m_dataType
,
913 connection
->m_sendingData
= NULL
;
915 return (DDERETURN
)data
;
923 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
927 wxString item_name
= DDEStringFromAtom(hsz2
);
929 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
930 len
= (DWORD
)ceil( double(len
) / sizeof(wxChar
) );
932 wxChar
*data
= connection
->GetBufferAtLeast( len
);
933 wxASSERT_MSG(data
!= NULL
,
934 _T("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
936 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
938 DdeFreeDataHandle(hData
);
939 if ( connection
->OnAdvise(connection
->m_topicName
,
943 (wxIPCFormat
) wFmt
) )
945 return (DDERETURN
)(DWORD
)DDE_FACK
;
949 return (DDERETURN
)DDE_FNOTPROCESSED
;
956 // ----------------------------------------------------------------------------
957 // DDE strings and atoms
958 // ----------------------------------------------------------------------------
961 static HSZ
DDEAddAtom(const wxString
& str
)
963 HSZ atom
= DDEAtomFromString(str
);
964 wxAtomTable
[str
] = atom
;
968 static HSZ
DDEGetAtom(const wxString
& str
)
970 wxAtomMap::iterator it
= wxAtomTable
.find(str
);
972 if (it
!= wxAtomTable
.end())
975 return DDEAddAtom(str
);
979 The returned handle has to be freed by the caller (using
980 (static) DDEFreeString).
982 static HSZ
DDEAtomFromString(const wxString
& s
)
984 wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") );
986 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, (wxChar
*) s
.c_str(), DDE_CP
);
989 DDELogError(_("Failed to create DDE string"));
995 static wxString
DDEStringFromAtom(HSZ hsz
)
997 // all DDE strings are normally limited to 255 bytes
998 static const size_t len
= 256;
1001 (void)DdeQueryString(DDEIdInst
, hsz
, wxStringBuffer(s
, len
), len
, DDE_CP
);
1006 static void DDEFreeString(HSZ hsz
)
1008 // DS: Failure to free a string handle might indicate there's
1009 // some other severe error.
1010 bool ok
= (::DdeFreeStringHandle(DDEIdInst
, hsz
) != 0);
1011 wxASSERT_MSG( ok
, wxT("Failed to free DDE string handle") );
1015 // ----------------------------------------------------------------------------
1017 // ----------------------------------------------------------------------------
1019 static void DDELogError(const wxString
& s
, UINT error
)
1023 error
= DdeGetLastError(DDEIdInst
);
1026 wxLogError(s
+ _T(": ") + DDEGetErrorMsg(error
));
1029 static wxString
DDEGetErrorMsg(UINT error
)
1034 case DMLERR_NO_ERROR
:
1035 err
= _("no DDE error.");
1038 case DMLERR_ADVACKTIMEOUT
:
1039 err
= _("a request for a synchronous advise transaction has timed out.");
1042 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
1044 case DMLERR_DATAACKTIMEOUT
:
1045 err
= _("a request for a synchronous data transaction has timed out.");
1047 case DMLERR_DLL_NOT_INITIALIZED
:
1048 err
= _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
1050 case DMLERR_DLL_USAGE
:
1051 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.");
1053 case DMLERR_EXECACKTIMEOUT
:
1054 err
= _("a request for a synchronous execute transaction has timed out.");
1056 case DMLERR_INVALIDPARAMETER
:
1057 err
= _("a parameter failed to be validated by the DDEML.");
1059 case DMLERR_LOW_MEMORY
:
1060 err
= _("a DDEML application has created a prolonged race condition.");
1062 case DMLERR_MEMORY_ERROR
:
1063 err
= _("a memory allocation failed.");
1065 case DMLERR_NO_CONV_ESTABLISHED
:
1066 err
= _("a client's attempt to establish a conversation has failed.");
1068 case DMLERR_NOTPROCESSED
:
1069 err
= _("a transaction failed.");
1071 case DMLERR_POKEACKTIMEOUT
:
1072 err
= _("a request for a synchronous poke transaction has timed out.");
1074 case DMLERR_POSTMSG_FAILED
:
1075 err
= _("an internal call to the PostMessage function has failed. ");
1077 case DMLERR_REENTRANCY
:
1078 err
= _("reentrancy problem.");
1080 case DMLERR_SERVER_DIED
:
1081 err
= _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1083 case DMLERR_SYS_ERROR
:
1084 err
= _("an internal error has occurred in the DDEML.");
1086 case DMLERR_UNADVACKTIMEOUT
:
1087 err
= _("a request to end an advise transaction has timed out.");
1089 case DMLERR_UNFOUND_QUEUE_ID
:
1090 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.");
1093 err
.Printf(_("Unknown DDE error %08x"), error
);