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 // some compilers headers don't define this one (mingw32)
53 #ifndef DMLERR_NO_ERROR
54 #define DMLERR_NO_ERROR (0)
56 // this one is also missing from some mingw32 headers, but there is no way
57 // to test for it (I know of) - the test for DMLERR_NO_ERROR works for me,
58 // but is surely not the right thing to do
60 HDDEDATA STDCALL
DdeClientTransaction(LPBYTE pData
,
68 #endif // no DMLERR_NO_ERROR
70 // ----------------------------------------------------------------------------
71 // macros and constants
72 // ----------------------------------------------------------------------------
77 #define _EXPORT _export
81 #define DDE_CP CP_WINUNICODE
83 #define DDE_CP CP_WINANSI
86 #define GetHConv() ((HCONV)m_hConv)
88 // default timeout for DDE operations (5sec)
89 #define DDE_TIMEOUT 5000
91 // ----------------------------------------------------------------------------
93 // ----------------------------------------------------------------------------
95 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
96 static void DDEDeleteConnection(HCONV hConv
);
97 static wxDDEServer
*DDEFindServer(const wxString
& s
);
99 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(WORD wType
,
108 // Add topic name to atom table before using in conversations
109 static HSZ
DDEAddAtom(const wxString
& string
);
110 static HSZ
DDEGetAtom(const wxString
& string
);
113 static HSZ
DDEAtomFromString(const wxString
& s
);
114 static wxString
DDEStringFromAtom(HSZ hsz
);
115 static void DDEFreeString(HSZ hsz
);
118 static wxString
DDEGetErrorMsg(UINT error
);
119 static void DDELogError(const wxString
& s
, UINT error
= DMLERR_NO_ERROR
);
121 // ----------------------------------------------------------------------------
123 // ----------------------------------------------------------------------------
125 WX_DECLARE_STRING_HASH_MAP( HSZ
, wxAtomMap
);
127 static DWORD DDEIdInst
= 0L;
128 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
129 static wxAtomMap wxAtomTable
;
131 #include "wx/listimpl.cpp"
133 WX_DEFINE_LIST(wxDDEClientList
);
134 WX_DEFINE_LIST(wxDDEServerList
);
135 WX_DEFINE_LIST(wxDDEConnectionList
);
137 static wxDDEClientList wxDDEClientObjects
;
138 static wxDDEServerList wxDDEServerObjects
;
140 static bool DDEInitialized
= false;
142 // ----------------------------------------------------------------------------
144 // ----------------------------------------------------------------------------
146 // A module to allow DDE cleanup without calling these functions
147 // from app.cpp or from the user's application.
149 class wxDDEModule
: public wxModule
153 bool OnInit() { return true; }
154 void OnExit() { wxDDECleanUp(); }
157 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
160 // ----------------------------------------------------------------------------
162 // ----------------------------------------------------------------------------
164 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
165 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
166 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
167 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
169 // ============================================================================
171 // ============================================================================
173 // ----------------------------------------------------------------------------
174 // initialization and cleanup
175 // ----------------------------------------------------------------------------
177 extern void wxDDEInitialize()
179 if ( !DDEInitialized
)
181 // Should insert filter flags
182 PFNCALLBACK callback
= (PFNCALLBACK
)
183 MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance());
184 UINT rc
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L);
185 if ( rc
!= DMLERR_NO_ERROR
)
187 DDELogError(_T("Failed to initialize DDE"), rc
);
191 DDEInitialized
= true;
198 // deleting them later won't work as DDE won't be initialized any more
199 wxASSERT_MSG( wxDDEServerObjects
.empty() &&
200 wxDDEClientObjects
.empty(),
201 _T("all DDE objects should be deleted by now") );
205 if ( DDEIdInst
!= 0 )
207 DdeUninitialize(DDEIdInst
);
212 // ----------------------------------------------------------------------------
213 // functions working with the global connection list(s)
214 // ----------------------------------------------------------------------------
216 // Global find connection
217 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
219 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
220 wxDDEConnection
*found
= NULL
;
221 while (serverNode
&& !found
)
223 wxDDEServer
*object
= serverNode
->GetData();
224 found
= object
->FindConnection((WXHCONV
) hConv
);
225 serverNode
= serverNode
->GetNext();
233 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
234 while (clientNode
&& !found
)
236 wxDDEClient
*object
= clientNode
->GetData();
237 found
= object
->FindConnection((WXHCONV
) hConv
);
238 clientNode
= clientNode
->GetNext();
243 // Global delete connection
244 static void DDEDeleteConnection(HCONV hConv
)
246 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
248 while (serverNode
&& !found
)
250 wxDDEServer
*object
= serverNode
->GetData();
251 found
= object
->DeleteConnection((WXHCONV
) hConv
);
252 serverNode
= serverNode
->GetNext();
259 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
260 while (clientNode
&& !found
)
262 wxDDEClient
*object
= clientNode
->GetData();
263 found
= object
->DeleteConnection((WXHCONV
) hConv
);
264 clientNode
= clientNode
->GetNext();
268 // Find a server from a service name
269 static wxDDEServer
*DDEFindServer(const wxString
& s
)
271 wxDDEServerList::compatibility_iterator node
= wxDDEServerObjects
.GetFirst();
272 wxDDEServer
*found
= NULL
;
273 while (node
&& !found
)
275 wxDDEServer
*object
= node
->GetData();
277 if (object
->GetServiceName() == s
)
283 node
= node
->GetNext();
290 // ----------------------------------------------------------------------------
292 // ----------------------------------------------------------------------------
294 wxDDEServer::wxDDEServer()
298 wxDDEServerObjects
.Append(this);
301 bool wxDDEServer::Create(const wxString
& server
)
303 m_serviceName
= server
;
305 HSZ hsz
= DDEAtomFromString(server
);
313 bool success
= (DdeNameService(DDEIdInst
, hsz
, (HSZ
) NULL
, DNS_REGISTER
)
318 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
327 wxDDEServer::~wxDDEServer()
329 if ( !!m_serviceName
)
331 HSZ hsz
= DDEAtomFromString(m_serviceName
);
335 if ( !DdeNameService(DDEIdInst
, hsz
,
336 (HSZ
) NULL
, DNS_UNREGISTER
) )
338 DDELogError(wxString::Format(
339 _("Failed to unregister DDE server '%s'"),
340 m_serviceName
.c_str()));
347 wxDDEServerObjects
.DeleteObject(this);
349 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
352 wxDDEConnection
*connection
= node
->GetData();
353 wxDDEConnectionList::compatibility_iterator next
= node
->GetNext();
354 connection
->SetConnected(false);
355 connection
->OnDisconnect(); // May delete the node implicitly
359 // If any left after this, delete them
360 node
= m_connections
.GetFirst();
363 wxDDEConnection
*connection
= node
->GetData();
364 wxDDEConnectionList::compatibility_iterator next
= node
->GetNext();
370 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
372 return new wxDDEConnection
;
375 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
377 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
378 wxDDEConnection
*found
= NULL
;
379 while (node
&& !found
)
381 wxDDEConnection
*connection
= node
->GetData();
382 if (connection
->m_hConv
== conv
)
384 else node
= node
->GetNext();
389 // Only delete the entry in the map, not the actual connection
390 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
392 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
395 wxDDEConnection
*connection
= node
->GetData();
396 if (connection
->m_hConv
== conv
)
398 m_connections
.Erase(node
);
403 node
= node
->GetNext();
409 // ----------------------------------------------------------------------------
411 // ----------------------------------------------------------------------------
413 wxDDEClient::wxDDEClient()
417 wxDDEClientObjects
.Append(this);
420 wxDDEClient::~wxDDEClient()
422 wxDDEClientObjects
.DeleteObject(this);
423 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
426 wxDDEConnection
*connection
= node
->GetData();
427 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
428 node
= m_connections
.GetFirst();
432 bool wxDDEClient::ValidHost(const wxString
& /* host */)
437 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
),
438 const wxString
& server
,
439 const wxString
& topic
)
441 HSZ hszServer
= DDEAtomFromString(server
);
445 return (wxConnectionBase
*) NULL
;
449 HSZ hszTopic
= DDEAtomFromString(topic
);
453 DDEFreeString(hszServer
);
454 return (wxConnectionBase
*) NULL
;
458 HCONV hConv
= ::DdeConnect(DDEIdInst
, hszServer
, hszTopic
,
459 (PCONVCONTEXT
) NULL
);
461 DDEFreeString(hszServer
);
462 DDEFreeString(hszTopic
);
467 DDELogError( wxString::Format(
468 _("Failed to create connection to server '%s' on topic '%s'"),
469 server
.c_str(), topic
.c_str()) );
473 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
476 connection
->m_hConv
= (WXHCONV
) hConv
;
477 connection
->m_topicName
= topic
;
478 connection
->m_client
= this;
479 m_connections
.Append(connection
);
484 return (wxConnectionBase
*) NULL
;
487 wxConnectionBase
*wxDDEClient::OnMakeConnection()
489 return new wxDDEConnection
;
492 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
494 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
495 wxDDEConnection
*found
= NULL
;
496 while (node
&& !found
)
498 wxDDEConnection
*connection
= node
->GetData();
499 if (connection
->m_hConv
== conv
)
501 else node
= node
->GetNext();
506 // Only delete the entry in the map, not the actual connection
507 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
509 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
512 wxDDEConnection
*connection
= node
->GetData();
513 if (connection
->m_hConv
== conv
)
515 m_connections
.Erase(node
);
518 else node
= node
->GetNext();
523 // ----------------------------------------------------------------------------
525 // ----------------------------------------------------------------------------
527 wxDDEConnection::wxDDEConnection(wxChar
*buffer
, int size
)
528 : wxConnectionBase(buffer
, size
)
534 m_sendingData
= NULL
;
537 wxDDEConnection::wxDDEConnection()
541 m_sendingData
= NULL
;
546 wxDDEConnection::~wxDDEConnection()
550 m_server
->GetConnections().DeleteObject(this);
552 m_client
->GetConnections().DeleteObject(this);
555 // Calls that CLIENT can make
556 bool wxDDEConnection::Disconnect()
558 if ( !GetConnected() )
561 DDEDeleteConnection(GetHConv());
563 bool ok
= DdeDisconnect(GetHConv()) != 0;
566 DDELogError(_T("Failed to disconnect from DDE server gracefully"));
569 SetConnected( false ); // so we don't try and disconnect again
574 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
579 size
= wxStrlen(data
) + 1;
582 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
592 DDELogError(_T("DDE execute request failed"));
598 wxChar
*wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
602 HSZ atom
= DDEGetAtom(item
);
604 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
610 if ( !returned_data
)
612 DDELogError(_T("DDE data request failed"));
617 DWORD len
= DdeGetData(returned_data
, NULL
, 0, 0);
619 wxChar
*data
= GetBufferAtLeast( len
);
620 wxASSERT_MSG(data
!= NULL
,
621 _T("Buffer too small in wxDDEConnection::Request") );
622 (void) DdeGetData(returned_data
, (LPBYTE
)data
, len
, 0);
624 (void) DdeFreeDataHandle(returned_data
);
632 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
637 size
= wxStrlen(data
) + 1;
640 HSZ item_atom
= DDEGetAtom(item
);
641 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
649 DDELogError(_("DDE poke request failed"));
655 bool wxDDEConnection::StartAdvise(const wxString
& item
)
658 HSZ atom
= DDEGetAtom(item
);
660 bool ok
= DdeClientTransaction(NULL
, 0,
668 DDELogError(_("Failed to establish an advise loop with DDE server"));
674 bool wxDDEConnection::StopAdvise(const wxString
& item
)
677 HSZ atom
= DDEGetAtom(item
);
679 bool ok
= DdeClientTransaction(NULL
, 0,
687 DDELogError(_("Failed to terminate the advise loop with DDE server"));
693 // Calls that SERVER can make
694 bool wxDDEConnection::Advise(const wxString
& item
,
701 size
= wxStrlen(data
) + 1;
704 HSZ item_atom
= DDEGetAtom(item
);
705 HSZ topic_atom
= DDEGetAtom(m_topicName
);
706 m_sendingData
= data
; // mrf: potential for scope problems here?
710 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
713 DDELogError(_("Failed to send DDE advise notification"));
719 bool wxDDEConnection::OnDisconnect()
725 // ----------------------------------------------------------------------------
727 // ----------------------------------------------------------------------------
729 #define DDERETURN HDDEDATA
731 HDDEDATA EXPENTRY _EXPORT
732 _DDECallback(WORD wType
,
738 DWORD
WXUNUSED(lData1
),
739 DWORD
WXUNUSED(lData2
))
745 wxString topic
= DDEStringFromAtom(hsz1
),
746 srv
= DDEStringFromAtom(hsz2
);
747 wxDDEServer
*server
= DDEFindServer(srv
);
750 wxDDEConnection
*connection
=
751 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
754 connection
->m_server
= server
;
755 server
->GetConnections().Append(connection
);
756 connection
->m_hConv
= 0;
757 connection
->m_topicName
= topic
;
758 DDECurrentlyConnecting
= connection
;
759 return (DDERETURN
)(DWORD
)true;
765 case XTYP_CONNECT_CONFIRM
:
767 if (DDECurrentlyConnecting
)
769 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
770 DDECurrentlyConnecting
= NULL
;
771 return (DDERETURN
)(DWORD
)true;
776 case XTYP_DISCONNECT
:
778 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
781 connection
->SetConnected( false );
782 if (connection
->OnDisconnect())
784 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
785 return (DDERETURN
)(DWORD
)true;
793 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
797 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
799 wxChar
*data
= connection
->GetBufferAtLeast( len
);
800 wxASSERT_MSG(data
!= NULL
,
801 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
803 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
805 DdeFreeDataHandle(hData
);
807 if ( connection
->OnExecute(connection
->m_topicName
,
810 (wxIPCFormat
) wFmt
) )
812 return (DDERETURN
)(DWORD
)DDE_FACK
;
816 return (DDERETURN
)DDE_FNOTPROCESSED
;
821 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
825 wxString item_name
= DDEStringFromAtom(hsz2
);
828 wxChar
*data
= connection
->OnRequest(connection
->m_topicName
,
835 user_size
= wxStrlen((wxChar
*)data
) + 1;
837 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
844 return (DDERETURN
)handle
;
852 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
856 wxString item_name
= DDEStringFromAtom(hsz2
);
858 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
860 wxChar
*data
= connection
->GetBufferAtLeast( len
);
861 wxASSERT_MSG(data
!= NULL
,
862 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
864 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
866 DdeFreeDataHandle(hData
);
868 connection
->OnPoke(connection
->m_topicName
,
874 return (DDERETURN
)DDE_FACK
;
878 return (DDERETURN
)DDE_FNOTPROCESSED
;
884 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
888 wxString item_name
= DDEStringFromAtom(hsz2
);
890 return (DDERETURN
)connection
->
891 OnStartAdvise(connection
->m_topicName
, item_name
);
899 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
903 wxString item_name
= DDEStringFromAtom(hsz2
);
905 return (DDERETURN
)connection
->
906 OnStopAdvise(connection
->m_topicName
, item_name
);
914 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
916 if (connection
&& connection
->m_sendingData
)
918 HDDEDATA data
= DdeCreateDataHandle
921 (LPBYTE
)connection
->m_sendingData
,
922 connection
->m_dataSize
,
925 connection
->m_dataType
,
929 connection
->m_sendingData
= NULL
;
931 return (DDERETURN
)data
;
939 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
943 wxString item_name
= DDEStringFromAtom(hsz2
);
945 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
947 wxChar
*data
= connection
->GetBufferAtLeast( len
);
948 wxASSERT_MSG(data
!= NULL
,
949 _T("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
951 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
953 DdeFreeDataHandle(hData
);
954 if ( connection
->OnAdvise(connection
->m_topicName
,
958 (wxIPCFormat
) wFmt
) )
960 return (DDERETURN
)(DWORD
)DDE_FACK
;
964 return (DDERETURN
)DDE_FNOTPROCESSED
;
971 // ----------------------------------------------------------------------------
972 // DDE strings and atoms
973 // ----------------------------------------------------------------------------
976 static HSZ
DDEAddAtom(const wxString
& str
)
978 HSZ atom
= DDEAtomFromString(str
);
979 wxAtomTable
[str
] = atom
;
983 static HSZ
DDEGetAtom(const wxString
& str
)
985 wxAtomMap::iterator it
= wxAtomTable
.find(str
);
987 if (it
!= wxAtomTable
.end())
990 return DDEAddAtom(str
);
994 The returned handle has to be freed by the caller (using
995 (static) DDEFreeString).
997 static HSZ
DDEAtomFromString(const wxString
& s
)
999 wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") );
1001 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, (wxChar
*) s
.c_str(), DDE_CP
);
1004 DDELogError(_("Failed to create DDE string"));
1010 static wxString
DDEStringFromAtom(HSZ hsz
)
1012 // all DDE strings are normally limited to 255 bytes
1013 static const size_t len
= 256;
1016 (void)DdeQueryString(DDEIdInst
, hsz
, wxStringBuffer(s
, len
), len
, DDE_CP
);
1021 static void DDEFreeString(HSZ hsz
)
1023 // DS: Failure to free a string handle might indicate there's
1024 // some other severe error.
1025 bool ok
= (::DdeFreeStringHandle(DDEIdInst
, hsz
) != 0);
1026 wxASSERT_MSG( ok
, wxT("Failed to free DDE string handle") );
1030 // ----------------------------------------------------------------------------
1032 // ----------------------------------------------------------------------------
1034 static void DDELogError(const wxString
& s
, UINT error
)
1038 error
= DdeGetLastError(DDEIdInst
);
1041 wxLogError(s
+ _T(": ") + DDEGetErrorMsg(error
));
1044 static wxString
DDEGetErrorMsg(UINT error
)
1049 case DMLERR_NO_ERROR
:
1050 err
= _("no DDE error.");
1053 case DMLERR_ADVACKTIMEOUT
:
1054 err
= _("a request for a synchronous advise transaction has timed out.");
1057 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
1059 case DMLERR_DATAACKTIMEOUT
:
1060 err
= _("a request for a synchronous data transaction has timed out.");
1062 case DMLERR_DLL_NOT_INITIALIZED
:
1063 err
= _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
1065 case DMLERR_DLL_USAGE
:
1066 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.");
1068 case DMLERR_EXECACKTIMEOUT
:
1069 err
= _("a request for a synchronous execute transaction has timed out.");
1071 case DMLERR_INVALIDPARAMETER
:
1072 err
= _("a parameter failed to be validated by the DDEML.");
1074 case DMLERR_LOW_MEMORY
:
1075 err
= _("a DDEML application has created a prolonged race condition.");
1077 case DMLERR_MEMORY_ERROR
:
1078 err
= _("a memory allocation failed.");
1080 case DMLERR_NO_CONV_ESTABLISHED
:
1081 err
= _("a client's attempt to establish a conversation has failed.");
1083 case DMLERR_NOTPROCESSED
:
1084 err
= _("a transaction failed.");
1086 case DMLERR_POKEACKTIMEOUT
:
1087 err
= _("a request for a synchronous poke transaction has timed out.");
1089 case DMLERR_POSTMSG_FAILED
:
1090 err
= _("an internal call to the PostMessage function has failed. ");
1092 case DMLERR_REENTRANCY
:
1093 err
= _("reentrancy problem.");
1095 case DMLERR_SERVER_DIED
:
1096 err
= _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1098 case DMLERR_SYS_ERROR
:
1099 err
= _("an internal error has occurred in the DDEML.");
1101 case DMLERR_UNADVACKTIMEOUT
:
1102 err
= _("a request to end an advise transaction has timed out.");
1104 case DMLERR_UNFOUND_QUEUE_ID
:
1105 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.");
1108 err
.Printf(_("Unknown DDE error %08x"), error
);