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"
43 #include "wx/msw/private.h"
48 // ----------------------------------------------------------------------------
49 // macros and constants
50 // ----------------------------------------------------------------------------
55 #define _EXPORT _export
59 #define DDE_CP CP_WINUNICODE
61 #define DDE_CP CP_WINANSI
64 #define GetHConv() ((HCONV)m_hConv)
66 // default timeout for DDE operations (5sec)
67 #define DDE_TIMEOUT 5000
69 // ----------------------------------------------------------------------------
71 // ----------------------------------------------------------------------------
73 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
74 static void DDEDeleteConnection(HCONV hConv
);
75 static wxDDEServer
*DDEFindServer(const wxString
& s
);
77 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(WORD wType
,
86 // Add topic name to atom table before using in conversations
87 static HSZ
DDEAddAtom(const wxString
& string
);
88 static HSZ
DDEGetAtom(const wxString
& string
);
91 static HSZ
DDEAtomFromString(const wxString
& s
);
92 static wxString
DDEStringFromAtom(HSZ hsz
);
93 static void DDEFreeString(HSZ hsz
);
96 static wxString
DDEGetErrorMsg(UINT error
);
97 static void DDELogError(const wxString
& s
, UINT error
= DMLERR_NO_ERROR
);
99 // ----------------------------------------------------------------------------
101 // ----------------------------------------------------------------------------
103 WX_DECLARE_STRING_HASH_MAP( HSZ
, wxAtomMap
);
105 static DWORD DDEIdInst
= 0L;
106 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
107 static wxAtomMap wxAtomTable
;
109 #include "wx/listimpl.cpp"
111 WX_DEFINE_LIST(wxDDEClientList
);
112 WX_DEFINE_LIST(wxDDEServerList
);
113 WX_DEFINE_LIST(wxDDEConnectionList
);
115 static wxDDEClientList wxDDEClientObjects
;
116 static wxDDEServerList wxDDEServerObjects
;
118 static bool DDEInitialized
= false;
120 // ----------------------------------------------------------------------------
122 // ----------------------------------------------------------------------------
124 // A module to allow DDE cleanup without calling these functions
125 // from app.cpp or from the user's application.
127 class wxDDEModule
: public wxModule
131 bool OnInit() { return true; }
132 void OnExit() { wxDDECleanUp(); }
135 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
138 // ----------------------------------------------------------------------------
140 // ----------------------------------------------------------------------------
142 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
143 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
144 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
145 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
147 // ============================================================================
149 // ============================================================================
151 // ----------------------------------------------------------------------------
152 // initialization and cleanup
153 // ----------------------------------------------------------------------------
155 extern void wxDDEInitialize()
157 if ( !DDEInitialized
)
159 // Should insert filter flags
160 PFNCALLBACK callback
= (PFNCALLBACK
)
161 MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance());
162 UINT rc
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L);
163 if ( rc
!= DMLERR_NO_ERROR
)
165 DDELogError(_T("Failed to initialize DDE"), rc
);
169 DDEInitialized
= true;
176 // deleting them later won't work as DDE won't be initialized any more
177 wxASSERT_MSG( wxDDEServerObjects
.empty() &&
178 wxDDEClientObjects
.empty(),
179 _T("all DDE objects should be deleted by now") );
183 if ( DDEIdInst
!= 0 )
185 DdeUninitialize(DDEIdInst
);
190 // ----------------------------------------------------------------------------
191 // functions working with the global connection list(s)
192 // ----------------------------------------------------------------------------
194 // Global find connection
195 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
197 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
198 wxDDEConnection
*found
= NULL
;
199 while (serverNode
&& !found
)
201 wxDDEServer
*object
= serverNode
->GetData();
202 found
= object
->FindConnection((WXHCONV
) hConv
);
203 serverNode
= serverNode
->GetNext();
211 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
212 while (clientNode
&& !found
)
214 wxDDEClient
*object
= clientNode
->GetData();
215 found
= object
->FindConnection((WXHCONV
) hConv
);
216 clientNode
= clientNode
->GetNext();
221 // Global delete connection
222 static void DDEDeleteConnection(HCONV hConv
)
224 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
226 while (serverNode
&& !found
)
228 wxDDEServer
*object
= serverNode
->GetData();
229 found
= object
->DeleteConnection((WXHCONV
) hConv
);
230 serverNode
= serverNode
->GetNext();
237 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
238 while (clientNode
&& !found
)
240 wxDDEClient
*object
= clientNode
->GetData();
241 found
= object
->DeleteConnection((WXHCONV
) hConv
);
242 clientNode
= clientNode
->GetNext();
246 // Find a server from a service name
247 static wxDDEServer
*DDEFindServer(const wxString
& s
)
249 wxDDEServerList::compatibility_iterator node
= wxDDEServerObjects
.GetFirst();
250 wxDDEServer
*found
= NULL
;
251 while (node
&& !found
)
253 wxDDEServer
*object
= node
->GetData();
255 if (object
->GetServiceName() == s
)
261 node
= node
->GetNext();
268 // ----------------------------------------------------------------------------
270 // ----------------------------------------------------------------------------
272 wxDDEServer::wxDDEServer()
276 wxDDEServerObjects
.Append(this);
279 bool wxDDEServer::Create(const wxString
& server
)
281 m_serviceName
= server
;
283 HSZ hsz
= DDEAtomFromString(server
);
291 bool success
= (DdeNameService(DDEIdInst
, hsz
, (HSZ
) NULL
, DNS_REGISTER
)
296 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
305 wxDDEServer::~wxDDEServer()
307 if ( !m_serviceName
.IsEmpty() )
309 HSZ hsz
= DDEAtomFromString(m_serviceName
);
313 if ( !DdeNameService(DDEIdInst
, hsz
,
314 (HSZ
) NULL
, DNS_UNREGISTER
) )
316 DDELogError(wxString::Format(
317 _("Failed to unregister DDE server '%s'"),
318 m_serviceName
.c_str()));
325 wxDDEServerObjects
.DeleteObject(this);
327 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
330 wxDDEConnection
*connection
= node
->GetData();
331 wxDDEConnectionList::compatibility_iterator next
= node
->GetNext();
332 connection
->SetConnected(false);
333 connection
->OnDisconnect(); // May delete the node implicitly
337 // If any left after this, delete them
338 node
= m_connections
.GetFirst();
341 wxDDEConnection
*connection
= node
->GetData();
342 wxDDEConnectionList::compatibility_iterator next
= node
->GetNext();
348 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
350 return new wxDDEConnection
;
353 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
355 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
356 wxDDEConnection
*found
= NULL
;
357 while (node
&& !found
)
359 wxDDEConnection
*connection
= node
->GetData();
360 if (connection
->m_hConv
== conv
)
362 else node
= node
->GetNext();
367 // Only delete the entry in the map, not the actual connection
368 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
370 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
373 wxDDEConnection
*connection
= node
->GetData();
374 if (connection
->m_hConv
== conv
)
376 m_connections
.Erase(node
);
381 node
= node
->GetNext();
387 // ----------------------------------------------------------------------------
389 // ----------------------------------------------------------------------------
391 wxDDEClient::wxDDEClient()
395 wxDDEClientObjects
.Append(this);
398 wxDDEClient::~wxDDEClient()
400 wxDDEClientObjects
.DeleteObject(this);
401 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
404 wxDDEConnection
*connection
= node
->GetData();
405 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
406 node
= m_connections
.GetFirst();
410 bool wxDDEClient::ValidHost(const wxString
& /* host */)
415 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
),
416 const wxString
& server
,
417 const wxString
& topic
)
419 HSZ hszServer
= DDEAtomFromString(server
);
423 return (wxConnectionBase
*) NULL
;
427 HSZ hszTopic
= DDEAtomFromString(topic
);
431 DDEFreeString(hszServer
);
432 return (wxConnectionBase
*) NULL
;
436 HCONV hConv
= ::DdeConnect(DDEIdInst
, hszServer
, hszTopic
,
437 (PCONVCONTEXT
) NULL
);
439 DDEFreeString(hszServer
);
440 DDEFreeString(hszTopic
);
445 DDELogError( wxString::Format(
446 _("Failed to create connection to server '%s' on topic '%s'"),
447 server
.c_str(), topic
.c_str()) );
451 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
454 connection
->m_hConv
= (WXHCONV
) hConv
;
455 connection
->m_topicName
= topic
;
456 connection
->m_client
= this;
457 m_connections
.Append(connection
);
462 return (wxConnectionBase
*) NULL
;
465 wxConnectionBase
*wxDDEClient::OnMakeConnection()
467 return new wxDDEConnection
;
470 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
472 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
473 wxDDEConnection
*found
= NULL
;
474 while (node
&& !found
)
476 wxDDEConnection
*connection
= node
->GetData();
477 if (connection
->m_hConv
== conv
)
479 else node
= node
->GetNext();
484 // Only delete the entry in the map, not the actual connection
485 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
487 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
490 wxDDEConnection
*connection
= node
->GetData();
491 if (connection
->m_hConv
== conv
)
493 m_connections
.Erase(node
);
496 else node
= node
->GetNext();
501 // ----------------------------------------------------------------------------
503 // ----------------------------------------------------------------------------
505 wxDDEConnection::wxDDEConnection(wxChar
*buffer
, int size
)
506 : wxConnectionBase(buffer
, size
)
512 m_sendingData
= NULL
;
515 wxDDEConnection::wxDDEConnection()
519 m_sendingData
= NULL
;
524 wxDDEConnection::~wxDDEConnection()
528 m_server
->GetConnections().DeleteObject(this);
530 m_client
->GetConnections().DeleteObject(this);
533 // Calls that CLIENT can make
534 bool wxDDEConnection::Disconnect()
536 if ( !GetConnected() )
539 DDEDeleteConnection(GetHConv());
541 bool ok
= DdeDisconnect(GetHConv()) != 0;
544 DDELogError(_T("Failed to disconnect from DDE server gracefully"));
547 SetConnected( false ); // so we don't try and disconnect again
552 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
557 size
= wxStrlen(data
) + 1;
560 bool ok
= DdeClientTransaction((LPBYTE
)data
,
561 size
* sizeof(wxChar
),
571 DDELogError(_T("DDE execute request failed"));
577 wxChar
*wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
581 HSZ atom
= DDEGetAtom(item
);
583 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
589 if ( !returned_data
)
591 DDELogError(_T("DDE data request failed"));
596 DWORD len
= DdeGetData(returned_data
, NULL
, 0, 0);
598 wxChar
*data
= GetBufferAtLeast( len
/sizeof(wxChar
) );
599 wxASSERT_MSG(data
!= NULL
,
600 _T("Buffer too small in wxDDEConnection::Request") );
601 (void) DdeGetData(returned_data
, (LPBYTE
)data
, len
, 0);
603 (void) DdeFreeDataHandle(returned_data
);
606 *size
= (int)len
/sizeof(wxChar
);
611 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
616 size
= wxStrlen(data
) + 1;
619 HSZ item_atom
= DDEGetAtom(item
);
620 bool ok
= DdeClientTransaction((LPBYTE
)data
,
621 size
* sizeof(wxChar
),
629 DDELogError(_("DDE poke request failed"));
635 bool wxDDEConnection::StartAdvise(const wxString
& item
)
638 HSZ atom
= DDEGetAtom(item
);
640 bool ok
= DdeClientTransaction(NULL
, 0,
648 DDELogError(_("Failed to establish an advise loop with DDE server"));
654 bool wxDDEConnection::StopAdvise(const wxString
& item
)
657 HSZ atom
= DDEGetAtom(item
);
659 bool ok
= DdeClientTransaction(NULL
, 0,
667 DDELogError(_("Failed to terminate the advise loop with DDE server"));
673 // Calls that SERVER can make
674 bool wxDDEConnection::Advise(const wxString
& item
,
681 size
= wxStrlen(data
) + 1;
684 HSZ item_atom
= DDEGetAtom(item
);
685 HSZ topic_atom
= DDEGetAtom(m_topicName
);
686 m_sendingData
= data
; // mrf: potential for scope problems here?
690 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
693 DDELogError(_("Failed to send DDE advise notification"));
699 bool wxDDEConnection::OnDisconnect()
705 // ----------------------------------------------------------------------------
707 // ----------------------------------------------------------------------------
709 #define DDERETURN HDDEDATA
711 HDDEDATA EXPENTRY _EXPORT
712 _DDECallback(WORD wType
,
718 DWORD
WXUNUSED(lData1
),
719 DWORD
WXUNUSED(lData2
))
725 wxString topic
= DDEStringFromAtom(hsz1
),
726 srv
= DDEStringFromAtom(hsz2
);
727 wxDDEServer
*server
= DDEFindServer(srv
);
730 wxDDEConnection
*connection
=
731 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
734 connection
->m_server
= server
;
735 server
->GetConnections().Append(connection
);
736 connection
->m_hConv
= 0;
737 connection
->m_topicName
= topic
;
738 DDECurrentlyConnecting
= connection
;
739 return (DDERETURN
)(DWORD
)true;
745 case XTYP_CONNECT_CONFIRM
:
747 if (DDECurrentlyConnecting
)
749 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
750 DDECurrentlyConnecting
= NULL
;
751 return (DDERETURN
)(DWORD
)true;
756 case XTYP_DISCONNECT
:
758 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
761 connection
->SetConnected( false );
762 if (connection
->OnDisconnect())
764 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
765 return (DDERETURN
)(DWORD
)true;
773 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
777 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
779 wxChar
*data
= connection
->GetBufferAtLeast( len
/sizeof(wxChar
) );
780 wxASSERT_MSG(data
!= NULL
,
781 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
783 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
785 DdeFreeDataHandle(hData
);
787 if ( connection
->OnExecute(connection
->m_topicName
,
789 (int)len
/sizeof(wxChar
),
790 (wxIPCFormat
) wFmt
) )
792 return (DDERETURN
)(DWORD
)DDE_FACK
;
796 return (DDERETURN
)DDE_FNOTPROCESSED
;
801 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
805 wxString item_name
= DDEStringFromAtom(hsz2
);
808 wxChar
*data
= connection
->OnRequest(connection
->m_topicName
,
815 user_size
= wxStrlen((wxChar
*)data
) + 1;
817 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
819 user_size
*sizeof(wxChar
),
824 return (DDERETURN
)handle
;
832 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
836 wxString item_name
= DDEStringFromAtom(hsz2
);
838 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
840 wxChar
*data
= connection
->GetBufferAtLeast( len
/sizeof(wxChar
) );
841 wxASSERT_MSG(data
!= NULL
,
842 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
844 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
846 DdeFreeDataHandle(hData
);
848 connection
->OnPoke(connection
->m_topicName
,
851 (int)len
/sizeof(wxChar
),
854 return (DDERETURN
)DDE_FACK
;
858 return (DDERETURN
)DDE_FNOTPROCESSED
;
864 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
868 wxString item_name
= DDEStringFromAtom(hsz2
);
870 return (DDERETURN
)connection
->
871 OnStartAdvise(connection
->m_topicName
, item_name
);
879 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
883 wxString item_name
= DDEStringFromAtom(hsz2
);
885 return (DDERETURN
)connection
->
886 OnStopAdvise(connection
->m_topicName
, item_name
);
894 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
896 if (connection
&& connection
->m_sendingData
)
898 HDDEDATA data
= DdeCreateDataHandle
901 (LPBYTE
)connection
->m_sendingData
,
902 connection
->m_dataSize
*sizeof(wxChar
),
905 connection
->m_dataType
,
909 connection
->m_sendingData
= NULL
;
911 return (DDERETURN
)data
;
919 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
923 wxString item_name
= DDEStringFromAtom(hsz2
);
925 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
927 wxChar
*data
= connection
->GetBufferAtLeast( len
/sizeof(wxChar
) );
928 wxASSERT_MSG(data
!= NULL
,
929 _T("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
931 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
933 DdeFreeDataHandle(hData
);
934 if ( connection
->OnAdvise(connection
->m_topicName
,
937 (int)len
/sizeof(wxChar
),
938 (wxIPCFormat
) wFmt
) )
940 return (DDERETURN
)(DWORD
)DDE_FACK
;
944 return (DDERETURN
)DDE_FNOTPROCESSED
;
951 // ----------------------------------------------------------------------------
952 // DDE strings and atoms
953 // ----------------------------------------------------------------------------
956 static HSZ
DDEAddAtom(const wxString
& str
)
958 HSZ atom
= DDEAtomFromString(str
);
959 wxAtomTable
[str
] = atom
;
963 static HSZ
DDEGetAtom(const wxString
& str
)
965 wxAtomMap::iterator it
= wxAtomTable
.find(str
);
967 if (it
!= wxAtomTable
.end())
970 return DDEAddAtom(str
);
974 The returned handle has to be freed by the caller (using
975 (static) DDEFreeString).
977 static HSZ
DDEAtomFromString(const wxString
& s
)
979 wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") );
981 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, (wxChar
*) s
.c_str(), DDE_CP
);
984 DDELogError(_("Failed to create DDE string"));
990 static wxString
DDEStringFromAtom(HSZ hsz
)
992 // all DDE strings are normally limited to 255 bytes
993 static const size_t len
= 256;
996 (void)DdeQueryString(DDEIdInst
, hsz
, wxStringBuffer(s
, len
), len
, DDE_CP
);
1001 static void DDEFreeString(HSZ hsz
)
1003 // DS: Failure to free a string handle might indicate there's
1004 // some other severe error.
1005 bool ok
= (::DdeFreeStringHandle(DDEIdInst
, hsz
) != 0);
1006 wxASSERT_MSG( ok
, wxT("Failed to free DDE string handle") );
1010 // ----------------------------------------------------------------------------
1012 // ----------------------------------------------------------------------------
1014 static void DDELogError(const wxString
& s
, UINT error
)
1018 error
= DdeGetLastError(DDEIdInst
);
1021 wxLogError(s
+ _T(": ") + DDEGetErrorMsg(error
));
1024 static wxString
DDEGetErrorMsg(UINT error
)
1029 case DMLERR_NO_ERROR
:
1030 err
= _("no DDE error.");
1033 case DMLERR_ADVACKTIMEOUT
:
1034 err
= _("a request for a synchronous advise transaction has timed out.");
1037 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
1039 case DMLERR_DATAACKTIMEOUT
:
1040 err
= _("a request for a synchronous data transaction has timed out.");
1042 case DMLERR_DLL_NOT_INITIALIZED
:
1043 err
= _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
1045 case DMLERR_DLL_USAGE
:
1046 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.");
1048 case DMLERR_EXECACKTIMEOUT
:
1049 err
= _("a request for a synchronous execute transaction has timed out.");
1051 case DMLERR_INVALIDPARAMETER
:
1052 err
= _("a parameter failed to be validated by the DDEML.");
1054 case DMLERR_LOW_MEMORY
:
1055 err
= _("a DDEML application has created a prolonged race condition.");
1057 case DMLERR_MEMORY_ERROR
:
1058 err
= _("a memory allocation failed.");
1060 case DMLERR_NO_CONV_ESTABLISHED
:
1061 err
= _("a client's attempt to establish a conversation has failed.");
1063 case DMLERR_NOTPROCESSED
:
1064 err
= _("a transaction failed.");
1066 case DMLERR_POKEACKTIMEOUT
:
1067 err
= _("a request for a synchronous poke transaction has timed out.");
1069 case DMLERR_POSTMSG_FAILED
:
1070 err
= _("an internal call to the PostMessage function has failed. ");
1072 case DMLERR_REENTRANCY
:
1073 err
= _("reentrancy problem.");
1075 case DMLERR_SERVER_DIED
:
1076 err
= _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1078 case DMLERR_SYS_ERROR
:
1079 err
= _("an internal error has occurred in the DDEML.");
1081 case DMLERR_UNADVACKTIMEOUT
:
1082 err
= _("a request to end an advise transaction has timed out.");
1084 case DMLERR_UNFOUND_QUEUE_ID
:
1085 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.");
1088 err
.Printf(_("Unknown DDE error %08x"), error
);