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 #ifdef __GNUWIN32_OLD__
49 #include "wx/msw/gnuwin32/extra.h"
52 // ----------------------------------------------------------------------------
53 // macros and constants
54 // ----------------------------------------------------------------------------
59 #define _EXPORT _export
63 #define DDE_CP CP_WINUNICODE
65 #define DDE_CP CP_WINANSI
68 #define GetHConv() ((HCONV)m_hConv)
70 // default timeout for DDE operations (5sec)
71 #define DDE_TIMEOUT 5000
73 // ----------------------------------------------------------------------------
75 // ----------------------------------------------------------------------------
77 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
78 static void DDEDeleteConnection(HCONV hConv
);
79 static wxDDEServer
*DDEFindServer(const wxString
& s
);
81 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(WORD wType
,
90 // Add topic name to atom table before using in conversations
91 static HSZ
DDEAddAtom(const wxString
& string
);
92 static HSZ
DDEGetAtom(const wxString
& string
);
95 static HSZ
DDEAtomFromString(const wxString
& s
);
96 static wxString
DDEStringFromAtom(HSZ hsz
);
97 static void DDEFreeString(HSZ hsz
);
100 static wxString
DDEGetErrorMsg(UINT error
);
101 static void DDELogError(const wxString
& s
, UINT error
= DMLERR_NO_ERROR
);
103 // ----------------------------------------------------------------------------
105 // ----------------------------------------------------------------------------
107 WX_DECLARE_STRING_HASH_MAP( HSZ
, wxAtomMap
);
109 static DWORD DDEIdInst
= 0L;
110 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
111 static wxAtomMap wxAtomTable
;
113 #include "wx/listimpl.cpp"
115 WX_DEFINE_LIST(wxDDEClientList
);
116 WX_DEFINE_LIST(wxDDEServerList
);
117 WX_DEFINE_LIST(wxDDEConnectionList
);
119 static wxDDEClientList wxDDEClientObjects
;
120 static wxDDEServerList wxDDEServerObjects
;
122 static bool DDEInitialized
= false;
124 // ----------------------------------------------------------------------------
126 // ----------------------------------------------------------------------------
128 // A module to allow DDE cleanup without calling these functions
129 // from app.cpp or from the user's application.
131 class wxDDEModule
: public wxModule
135 bool OnInit() { return true; }
136 void OnExit() { wxDDECleanUp(); }
139 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
142 // ----------------------------------------------------------------------------
144 // ----------------------------------------------------------------------------
146 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
147 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
148 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
149 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
151 // ============================================================================
153 // ============================================================================
155 // ----------------------------------------------------------------------------
156 // initialization and cleanup
157 // ----------------------------------------------------------------------------
159 extern void wxDDEInitialize()
161 if ( !DDEInitialized
)
163 // Should insert filter flags
164 PFNCALLBACK callback
= (PFNCALLBACK
)
165 MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance());
166 UINT rc
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L);
167 if ( rc
!= DMLERR_NO_ERROR
)
169 DDELogError(_T("Failed to initialize DDE"), rc
);
173 DDEInitialized
= true;
180 // deleting them later won't work as DDE won't be initialized any more
181 wxASSERT_MSG( wxDDEServerObjects
.empty() &&
182 wxDDEClientObjects
.empty(),
183 _T("all DDE objects should be deleted by now") );
187 if ( DDEIdInst
!= 0 )
189 DdeUninitialize(DDEIdInst
);
194 // ----------------------------------------------------------------------------
195 // functions working with the global connection list(s)
196 // ----------------------------------------------------------------------------
198 // Global find connection
199 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
201 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
202 wxDDEConnection
*found
= NULL
;
203 while (serverNode
&& !found
)
205 wxDDEServer
*object
= serverNode
->GetData();
206 found
= object
->FindConnection((WXHCONV
) hConv
);
207 serverNode
= serverNode
->GetNext();
215 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
216 while (clientNode
&& !found
)
218 wxDDEClient
*object
= clientNode
->GetData();
219 found
= object
->FindConnection((WXHCONV
) hConv
);
220 clientNode
= clientNode
->GetNext();
225 // Global delete connection
226 static void DDEDeleteConnection(HCONV hConv
)
228 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
230 while (serverNode
&& !found
)
232 wxDDEServer
*object
= serverNode
->GetData();
233 found
= object
->DeleteConnection((WXHCONV
) hConv
);
234 serverNode
= serverNode
->GetNext();
241 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
242 while (clientNode
&& !found
)
244 wxDDEClient
*object
= clientNode
->GetData();
245 found
= object
->DeleteConnection((WXHCONV
) hConv
);
246 clientNode
= clientNode
->GetNext();
250 // Find a server from a service name
251 static wxDDEServer
*DDEFindServer(const wxString
& s
)
253 wxDDEServerList::compatibility_iterator node
= wxDDEServerObjects
.GetFirst();
254 wxDDEServer
*found
= NULL
;
255 while (node
&& !found
)
257 wxDDEServer
*object
= node
->GetData();
259 if (object
->GetServiceName() == s
)
265 node
= node
->GetNext();
272 // ----------------------------------------------------------------------------
274 // ----------------------------------------------------------------------------
276 wxDDEServer::wxDDEServer()
280 wxDDEServerObjects
.Append(this);
283 bool wxDDEServer::Create(const wxString
& server
)
285 m_serviceName
= server
;
287 HSZ hsz
= DDEAtomFromString(server
);
295 bool success
= (DdeNameService(DDEIdInst
, hsz
, (HSZ
) NULL
, DNS_REGISTER
)
300 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
309 wxDDEServer::~wxDDEServer()
311 if ( !!m_serviceName
)
313 HSZ hsz
= DDEAtomFromString(m_serviceName
);
317 if ( !DdeNameService(DDEIdInst
, hsz
,
318 (HSZ
) NULL
, DNS_UNREGISTER
) )
320 DDELogError(wxString::Format(
321 _("Failed to unregister DDE server '%s'"),
322 m_serviceName
.c_str()));
329 wxDDEServerObjects
.DeleteObject(this);
331 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
334 wxDDEConnection
*connection
= node
->GetData();
335 wxDDEConnectionList::compatibility_iterator next
= node
->GetNext();
336 connection
->SetConnected(false);
337 connection
->OnDisconnect(); // May delete the node implicitly
341 // If any left after this, delete them
342 node
= m_connections
.GetFirst();
345 wxDDEConnection
*connection
= node
->GetData();
346 wxDDEConnectionList::compatibility_iterator next
= node
->GetNext();
352 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
354 return new wxDDEConnection
;
357 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
359 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
360 wxDDEConnection
*found
= NULL
;
361 while (node
&& !found
)
363 wxDDEConnection
*connection
= node
->GetData();
364 if (connection
->m_hConv
== conv
)
366 else node
= node
->GetNext();
371 // Only delete the entry in the map, not the actual connection
372 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
374 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
377 wxDDEConnection
*connection
= node
->GetData();
378 if (connection
->m_hConv
== conv
)
380 m_connections
.Erase(node
);
385 node
= node
->GetNext();
391 // ----------------------------------------------------------------------------
393 // ----------------------------------------------------------------------------
395 wxDDEClient::wxDDEClient()
399 wxDDEClientObjects
.Append(this);
402 wxDDEClient::~wxDDEClient()
404 wxDDEClientObjects
.DeleteObject(this);
405 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
408 wxDDEConnection
*connection
= node
->GetData();
409 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
410 node
= m_connections
.GetFirst();
414 bool wxDDEClient::ValidHost(const wxString
& /* host */)
419 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
),
420 const wxString
& server
,
421 const wxString
& topic
)
423 HSZ hszServer
= DDEAtomFromString(server
);
427 return (wxConnectionBase
*) NULL
;
431 HSZ hszTopic
= DDEAtomFromString(topic
);
435 DDEFreeString(hszServer
);
436 return (wxConnectionBase
*) NULL
;
440 HCONV hConv
= ::DdeConnect(DDEIdInst
, hszServer
, hszTopic
,
441 (PCONVCONTEXT
) NULL
);
443 DDEFreeString(hszServer
);
444 DDEFreeString(hszTopic
);
449 DDELogError( wxString::Format(
450 _("Failed to create connection to server '%s' on topic '%s'"),
451 server
.c_str(), topic
.c_str()) );
455 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
458 connection
->m_hConv
= (WXHCONV
) hConv
;
459 connection
->m_topicName
= topic
;
460 connection
->m_client
= this;
461 m_connections
.Append(connection
);
466 return (wxConnectionBase
*) NULL
;
469 wxConnectionBase
*wxDDEClient::OnMakeConnection()
471 return new wxDDEConnection
;
474 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
476 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
477 wxDDEConnection
*found
= NULL
;
478 while (node
&& !found
)
480 wxDDEConnection
*connection
= node
->GetData();
481 if (connection
->m_hConv
== conv
)
483 else node
= node
->GetNext();
488 // Only delete the entry in the map, not the actual connection
489 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
491 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
494 wxDDEConnection
*connection
= node
->GetData();
495 if (connection
->m_hConv
== conv
)
497 m_connections
.Erase(node
);
500 else node
= node
->GetNext();
505 // ----------------------------------------------------------------------------
507 // ----------------------------------------------------------------------------
509 wxDDEConnection::wxDDEConnection(wxChar
*buffer
, int size
)
510 : wxConnectionBase(buffer
, size
)
516 m_sendingData
= NULL
;
519 wxDDEConnection::wxDDEConnection()
523 m_sendingData
= NULL
;
528 wxDDEConnection::~wxDDEConnection()
532 m_server
->GetConnections().DeleteObject(this);
534 m_client
->GetConnections().DeleteObject(this);
537 // Calls that CLIENT can make
538 bool wxDDEConnection::Disconnect()
540 if ( !GetConnected() )
543 DDEDeleteConnection(GetHConv());
545 bool ok
= DdeDisconnect(GetHConv()) != 0;
548 DDELogError(_T("Failed to disconnect from DDE server gracefully"));
551 SetConnected( false ); // so we don't try and disconnect again
556 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
561 size
= wxStrlen(data
) + 1;
564 bool ok
= DdeClientTransaction((LPBYTE
)data
,
565 size
* sizeof(wxChar
),
575 DDELogError(_T("DDE execute request failed"));
581 wxChar
*wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
585 HSZ atom
= DDEGetAtom(item
);
587 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
593 if ( !returned_data
)
595 DDELogError(_T("DDE data request failed"));
600 DWORD len
= DdeGetData(returned_data
, NULL
, 0, 0);
602 wxChar
*data
= GetBufferAtLeast( len
/sizeof(wxChar
) );
603 wxASSERT_MSG(data
!= NULL
,
604 _T("Buffer too small in wxDDEConnection::Request") );
605 (void) DdeGetData(returned_data
, (LPBYTE
)data
, len
, 0);
607 (void) DdeFreeDataHandle(returned_data
);
610 *size
= (int)len
/sizeof(wxChar
);
615 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
620 size
= wxStrlen(data
) + 1;
623 HSZ item_atom
= DDEGetAtom(item
);
624 bool ok
= DdeClientTransaction((LPBYTE
)data
,
625 size
* sizeof(wxChar
),
633 DDELogError(_("DDE poke request failed"));
639 bool wxDDEConnection::StartAdvise(const wxString
& item
)
642 HSZ atom
= DDEGetAtom(item
);
644 bool ok
= DdeClientTransaction(NULL
, 0,
652 DDELogError(_("Failed to establish an advise loop with DDE server"));
658 bool wxDDEConnection::StopAdvise(const wxString
& item
)
661 HSZ atom
= DDEGetAtom(item
);
663 bool ok
= DdeClientTransaction(NULL
, 0,
671 DDELogError(_("Failed to terminate the advise loop with DDE server"));
677 // Calls that SERVER can make
678 bool wxDDEConnection::Advise(const wxString
& item
,
685 size
= wxStrlen(data
) + 1;
688 HSZ item_atom
= DDEGetAtom(item
);
689 HSZ topic_atom
= DDEGetAtom(m_topicName
);
690 m_sendingData
= data
; // mrf: potential for scope problems here?
694 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
697 DDELogError(_("Failed to send DDE advise notification"));
703 bool wxDDEConnection::OnDisconnect()
709 // ----------------------------------------------------------------------------
711 // ----------------------------------------------------------------------------
713 #define DDERETURN HDDEDATA
715 HDDEDATA EXPENTRY _EXPORT
716 _DDECallback(WORD wType
,
722 DWORD
WXUNUSED(lData1
),
723 DWORD
WXUNUSED(lData2
))
729 wxString topic
= DDEStringFromAtom(hsz1
),
730 srv
= DDEStringFromAtom(hsz2
);
731 wxDDEServer
*server
= DDEFindServer(srv
);
734 wxDDEConnection
*connection
=
735 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
738 connection
->m_server
= server
;
739 server
->GetConnections().Append(connection
);
740 connection
->m_hConv
= 0;
741 connection
->m_topicName
= topic
;
742 DDECurrentlyConnecting
= connection
;
743 return (DDERETURN
)(DWORD
)true;
749 case XTYP_CONNECT_CONFIRM
:
751 if (DDECurrentlyConnecting
)
753 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
754 DDECurrentlyConnecting
= NULL
;
755 return (DDERETURN
)(DWORD
)true;
760 case XTYP_DISCONNECT
:
762 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
765 connection
->SetConnected( false );
766 if (connection
->OnDisconnect())
768 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
769 return (DDERETURN
)(DWORD
)true;
777 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
781 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
783 wxChar
*data
= connection
->GetBufferAtLeast( len
/sizeof(wxChar
) );
784 wxASSERT_MSG(data
!= NULL
,
785 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
787 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
789 DdeFreeDataHandle(hData
);
791 if ( connection
->OnExecute(connection
->m_topicName
,
793 (int)len
/sizeof(wxChar
),
794 (wxIPCFormat
) wFmt
) )
796 return (DDERETURN
)(DWORD
)DDE_FACK
;
800 return (DDERETURN
)DDE_FNOTPROCESSED
;
805 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
809 wxString item_name
= DDEStringFromAtom(hsz2
);
812 wxChar
*data
= connection
->OnRequest(connection
->m_topicName
,
819 user_size
= wxStrlen((wxChar
*)data
) + 1;
821 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
823 user_size
*sizeof(wxChar
),
828 return (DDERETURN
)handle
;
836 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
840 wxString item_name
= DDEStringFromAtom(hsz2
);
842 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
844 wxChar
*data
= connection
->GetBufferAtLeast( len
/sizeof(wxChar
) );
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
,
855 (int)len
/sizeof(wxChar
),
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);
931 wxChar
*data
= connection
->GetBufferAtLeast( len
/sizeof(wxChar
) );
932 wxASSERT_MSG(data
!= NULL
,
933 _T("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
935 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
937 DdeFreeDataHandle(hData
);
938 if ( connection
->OnAdvise(connection
->m_topicName
,
941 (int)len
/sizeof(wxChar
),
942 (wxIPCFormat
) wFmt
) )
944 return (DDERETURN
)(DWORD
)DDE_FACK
;
948 return (DDERETURN
)DDE_FNOTPROCESSED
;
955 // ----------------------------------------------------------------------------
956 // DDE strings and atoms
957 // ----------------------------------------------------------------------------
960 static HSZ
DDEAddAtom(const wxString
& str
)
962 HSZ atom
= DDEAtomFromString(str
);
963 wxAtomTable
[str
] = atom
;
967 static HSZ
DDEGetAtom(const wxString
& str
)
969 wxAtomMap::iterator it
= wxAtomTable
.find(str
);
971 if (it
!= wxAtomTable
.end())
974 return DDEAddAtom(str
);
978 The returned handle has to be freed by the caller (using
979 (static) DDEFreeString).
981 static HSZ
DDEAtomFromString(const wxString
& s
)
983 wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") );
985 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, (wxChar
*) s
.c_str(), DDE_CP
);
988 DDELogError(_("Failed to create DDE string"));
994 static wxString
DDEStringFromAtom(HSZ hsz
)
996 // all DDE strings are normally limited to 255 bytes
997 static const size_t len
= 256;
1000 (void)DdeQueryString(DDEIdInst
, hsz
, wxStringBuffer(s
, len
), len
, DDE_CP
);
1005 static void DDEFreeString(HSZ hsz
)
1007 // DS: Failure to free a string handle might indicate there's
1008 // some other severe error.
1009 bool ok
= (::DdeFreeStringHandle(DDEIdInst
, hsz
) != 0);
1010 wxASSERT_MSG( ok
, wxT("Failed to free DDE string handle") );
1014 // ----------------------------------------------------------------------------
1016 // ----------------------------------------------------------------------------
1018 static void DDELogError(const wxString
& s
, UINT error
)
1022 error
= DdeGetLastError(DDEIdInst
);
1025 wxLogError(s
+ _T(": ") + DDEGetErrorMsg(error
));
1028 static wxString
DDEGetErrorMsg(UINT error
)
1033 case DMLERR_NO_ERROR
:
1034 err
= _("no DDE error.");
1037 case DMLERR_ADVACKTIMEOUT
:
1038 err
= _("a request for a synchronous advise transaction has timed out.");
1041 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
1043 case DMLERR_DATAACKTIMEOUT
:
1044 err
= _("a request for a synchronous data transaction has timed out.");
1046 case DMLERR_DLL_NOT_INITIALIZED
:
1047 err
= _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
1049 case DMLERR_DLL_USAGE
:
1050 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.");
1052 case DMLERR_EXECACKTIMEOUT
:
1053 err
= _("a request for a synchronous execute transaction has timed out.");
1055 case DMLERR_INVALIDPARAMETER
:
1056 err
= _("a parameter failed to be validated by the DDEML.");
1058 case DMLERR_LOW_MEMORY
:
1059 err
= _("a DDEML application has created a prolonged race condition.");
1061 case DMLERR_MEMORY_ERROR
:
1062 err
= _("a memory allocation failed.");
1064 case DMLERR_NO_CONV_ESTABLISHED
:
1065 err
= _("a client's attempt to establish a conversation has failed.");
1067 case DMLERR_NOTPROCESSED
:
1068 err
= _("a transaction failed.");
1070 case DMLERR_POKEACKTIMEOUT
:
1071 err
= _("a request for a synchronous poke transaction has timed out.");
1073 case DMLERR_POSTMSG_FAILED
:
1074 err
= _("an internal call to the PostMessage function has failed. ");
1076 case DMLERR_REENTRANCY
:
1077 err
= _("reentrancy problem.");
1079 case DMLERR_SERVER_DIED
:
1080 err
= _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1082 case DMLERR_SYS_ERROR
:
1083 err
= _("an internal error has occurred in the DDEML.");
1085 case DMLERR_UNADVACKTIMEOUT
:
1086 err
= _("a request to end an advise transaction has timed out.");
1088 case DMLERR_UNFOUND_QUEUE_ID
:
1089 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.");
1092 err
.Printf(_("Unknown DDE error %08x"), error
);