1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/dde.cpp
3 // Purpose: DDE classes
4 // Author: Julian Smart
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
31 #include "wx/hashmap.h"
32 #include "wx/module.h"
37 #include "wx/buffer.h"
38 #include "wx/strconv.h"
40 #include "wx/msw/private.h"
45 // ----------------------------------------------------------------------------
46 // macros and constants
47 // ----------------------------------------------------------------------------
52 #define _EXPORT _export
56 #define DDE_CP CP_WINUNICODE
58 #define DDE_CP CP_WINANSI
61 #define GetHConv() ((HCONV)m_hConv)
63 // default timeout for DDE operations (5sec)
64 #define DDE_TIMEOUT 5000
66 // ----------------------------------------------------------------------------
68 // ----------------------------------------------------------------------------
70 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
71 static void DDEDeleteConnection(HCONV hConv
);
72 static wxDDEServer
*DDEFindServer(const wxString
& s
);
74 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(WORD wType
,
83 // Add topic name to atom table before using in conversations
84 static HSZ
DDEAddAtom(const wxString
& string
);
85 static HSZ
DDEGetAtom(const wxString
& string
);
88 static HSZ
DDEAtomFromString(const wxString
& s
);
89 static wxString
DDEStringFromAtom(HSZ hsz
);
90 static void DDEFreeString(HSZ hsz
);
93 static wxString
DDEGetErrorMsg(UINT error
);
94 static void DDELogError(const wxString
& s
, UINT error
= DMLERR_NO_ERROR
);
96 // ----------------------------------------------------------------------------
98 // ----------------------------------------------------------------------------
100 WX_DECLARE_STRING_HASH_MAP( HSZ
, wxAtomMap
);
102 static DWORD DDEIdInst
= 0L;
103 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
104 static wxAtomMap wxAtomTable
;
106 #include "wx/listimpl.cpp"
108 WX_DEFINE_LIST(wxDDEClientList
)
109 WX_DEFINE_LIST(wxDDEServerList
)
110 WX_DEFINE_LIST(wxDDEConnectionList
)
112 static wxDDEClientList wxDDEClientObjects
;
113 static wxDDEServerList wxDDEServerObjects
;
115 static bool DDEInitialized
= false;
117 // ----------------------------------------------------------------------------
119 // ----------------------------------------------------------------------------
121 // A module to allow DDE cleanup without calling these functions
122 // from app.cpp or from the user's application.
124 class wxDDEModule
: public wxModule
128 bool OnInit() { return true; }
129 void OnExit() { wxDDECleanUp(); }
132 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
135 // ----------------------------------------------------------------------------
137 // ----------------------------------------------------------------------------
139 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
140 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
141 IMPLEMENT_DYNAMIC_CLASS(wxDDEConnection
, wxConnectionBase
)
142 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
144 // ============================================================================
146 // ============================================================================
148 // ----------------------------------------------------------------------------
149 // initialization and cleanup
150 // ----------------------------------------------------------------------------
152 extern void wxDDEInitialize()
154 if ( !DDEInitialized
)
156 // Should insert filter flags
157 PFNCALLBACK callback
= (PFNCALLBACK
)
158 MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance());
159 UINT rc
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L);
160 if ( rc
!= DMLERR_NO_ERROR
)
162 DDELogError(wxT("Failed to initialize DDE"), rc
);
166 DDEInitialized
= true;
173 // deleting them later won't work as DDE won't be initialized any more
174 wxASSERT_MSG( wxDDEServerObjects
.empty() &&
175 wxDDEClientObjects
.empty(),
176 wxT("all DDE objects should be deleted by now") );
180 if ( DDEIdInst
!= 0 )
182 DdeUninitialize(DDEIdInst
);
187 // ----------------------------------------------------------------------------
188 // functions working with the global connection list(s)
189 // ----------------------------------------------------------------------------
191 // Global find connection
192 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
194 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
195 wxDDEConnection
*found
= NULL
;
196 while (serverNode
&& !found
)
198 wxDDEServer
*object
= serverNode
->GetData();
199 found
= object
->FindConnection((WXHCONV
) hConv
);
200 serverNode
= serverNode
->GetNext();
208 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
209 while (clientNode
&& !found
)
211 wxDDEClient
*object
= clientNode
->GetData();
212 found
= object
->FindConnection((WXHCONV
) hConv
);
213 clientNode
= clientNode
->GetNext();
218 // Global delete connection
219 static void DDEDeleteConnection(HCONV hConv
)
221 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
223 while (serverNode
&& !found
)
225 wxDDEServer
*object
= serverNode
->GetData();
226 found
= object
->DeleteConnection((WXHCONV
) hConv
);
227 serverNode
= serverNode
->GetNext();
234 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
235 while (clientNode
&& !found
)
237 wxDDEClient
*object
= clientNode
->GetData();
238 found
= object
->DeleteConnection((WXHCONV
) hConv
);
239 clientNode
= clientNode
->GetNext();
243 // Find a server from a service name
244 static wxDDEServer
*DDEFindServer(const wxString
& s
)
246 wxDDEServerList::compatibility_iterator node
= wxDDEServerObjects
.GetFirst();
247 wxDDEServer
*found
= NULL
;
248 while (node
&& !found
)
250 wxDDEServer
*object
= node
->GetData();
252 if (object
->GetServiceName() == s
)
258 node
= node
->GetNext();
265 // ----------------------------------------------------------------------------
267 // ----------------------------------------------------------------------------
269 wxDDEServer::wxDDEServer()
273 wxDDEServerObjects
.Append(this);
276 bool wxDDEServer::Create(const wxString
& server
)
278 m_serviceName
= server
;
280 HSZ hsz
= DDEAtomFromString(server
);
288 bool success
= (DdeNameService(DDEIdInst
, hsz
, (HSZ
) NULL
, DNS_REGISTER
)
293 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
302 wxDDEServer::~wxDDEServer()
304 if ( !m_serviceName
.empty() )
306 HSZ hsz
= DDEAtomFromString(m_serviceName
);
310 if ( !DdeNameService(DDEIdInst
, hsz
,
311 (HSZ
) NULL
, DNS_UNREGISTER
) )
313 DDELogError(wxString::Format(
314 _("Failed to unregister DDE server '%s'"),
315 m_serviceName
.c_str()));
322 wxDDEServerObjects
.DeleteObject(this);
324 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
327 wxDDEConnection
*connection
= node
->GetData();
328 wxDDEConnectionList::compatibility_iterator next
= node
->GetNext();
329 connection
->OnDisconnect(); // May delete the node implicitly
333 // If any left after this, delete them
334 node
= m_connections
.GetFirst();
337 wxDDEConnection
*connection
= node
->GetData();
338 wxDDEConnectionList::compatibility_iterator next
= node
->GetNext();
344 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
346 return new wxDDEConnection
;
349 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
351 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
352 wxDDEConnection
*found
= NULL
;
353 while (node
&& !found
)
355 wxDDEConnection
*connection
= node
->GetData();
356 if (connection
->m_hConv
== conv
)
358 else node
= node
->GetNext();
363 // Only delete the entry in the map, not the actual connection
364 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
366 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
369 wxDDEConnection
*connection
= node
->GetData();
370 if (connection
->m_hConv
== conv
)
372 m_connections
.Erase(node
);
377 node
= node
->GetNext();
383 // ----------------------------------------------------------------------------
385 // ----------------------------------------------------------------------------
387 wxDDEClient::wxDDEClient()
391 wxDDEClientObjects
.Append(this);
394 wxDDEClient::~wxDDEClient()
396 wxDDEClientObjects
.DeleteObject(this);
397 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
400 wxDDEConnection
*connection
= node
->GetData();
401 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
402 node
= m_connections
.GetFirst();
406 bool wxDDEClient::ValidHost(const wxString
& /* host */)
411 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
),
412 const wxString
& server
,
413 const wxString
& topic
)
415 HSZ hszServer
= DDEAtomFromString(server
);
423 HSZ hszTopic
= DDEAtomFromString(topic
);
427 DDEFreeString(hszServer
);
432 HCONV hConv
= ::DdeConnect(DDEIdInst
, hszServer
, hszTopic
,
433 (PCONVCONTEXT
) NULL
);
435 DDEFreeString(hszServer
);
436 DDEFreeString(hszTopic
);
441 DDELogError( wxString::Format(
442 _("Failed to create connection to server '%s' on topic '%s'"),
443 server
.c_str(), topic
.c_str()) );
447 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
450 connection
->m_hConv
= (WXHCONV
) hConv
;
451 connection
->m_topicName
= topic
;
452 connection
->m_client
= this;
453 m_connections
.Append(connection
);
461 wxConnectionBase
*wxDDEClient::OnMakeConnection()
463 return new wxDDEConnection
;
466 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
468 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
469 wxDDEConnection
*found
= NULL
;
470 while (node
&& !found
)
472 wxDDEConnection
*connection
= node
->GetData();
473 if (connection
->m_hConv
== conv
)
475 else node
= node
->GetNext();
480 // Only delete the entry in the map, not the actual connection
481 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
483 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
486 wxDDEConnection
*connection
= node
->GetData();
487 if (connection
->m_hConv
== conv
)
489 m_connections
.Erase(node
);
492 else node
= node
->GetNext();
497 // ----------------------------------------------------------------------------
499 // ----------------------------------------------------------------------------
501 wxDDEConnection::wxDDEConnection(void *buffer
, size_t size
)
502 : wxConnectionBase(buffer
, size
)
508 m_sendingData
= NULL
;
511 wxDDEConnection::wxDDEConnection()
515 m_sendingData
= NULL
;
520 wxDDEConnection::~wxDDEConnection()
524 m_server
->GetConnections().DeleteObject(this);
526 m_client
->GetConnections().DeleteObject(this);
529 // Calls that CLIENT can make
530 bool wxDDEConnection::Disconnect()
532 if ( !GetConnected() )
535 DDEDeleteConnection(GetHConv());
537 bool ok
= DdeDisconnect(GetHConv()) != 0;
540 DDELogError(wxT("Failed to disconnect from DDE server gracefully"));
543 SetConnected( false ); // so we don't try and disconnect again
549 wxDDEConnection::DoExecute(const void *data
, size_t size
, wxIPCFormat format
)
551 wxCHECK_MSG( format
== wxIPC_TEXT
||
552 format
== wxIPC_UTF8TEXT
||
553 format
== wxIPC_UNICODETEXT
,
555 wxT("wxDDEServer::Execute() supports only text data") );
557 wxMemoryBuffer buffer
;
558 LPBYTE realData
= NULL
;
560 wxMBConv
*conv
= NULL
;
562 // Windows only supports either ANSI or UTF-16 format depending on the
563 // build, so we need to convert the data if it doesn't use it already
565 if ( format
== wxIPC_TEXT
)
569 else if ( format
== wxIPC_UTF8TEXT
)
573 else // no conversion necessary for wxIPC_UNICODETEXT
575 realData
= (LPBYTE
)data
;
581 const char * const text
= (const char *)data
;
582 const size_t len
= size
;
584 realSize
= conv
->ToWChar(NULL
, 0, text
, len
);
585 if ( realSize
== wxCONV_FAILED
)
588 realData
= (LPBYTE
)buffer
.GetWriteBuf(realSize
*sizeof(wchar_t));
592 realSize
= conv
->ToWChar((wchar_t *)realData
, realSize
, text
, len
);
593 if ( realSize
== wxCONV_FAILED
)
596 // We need to pass the size of the buffer to DdeClientTransaction() and
597 // not the length of the string.
598 realSize
*= sizeof(wchar_t);
600 #else // !wxUSE_UNICODE
601 if ( format
== wxIPC_UNICODETEXT
)
605 else if ( format
== wxIPC_UTF8TEXT
)
607 // we could implement this in theory but it's not obvious how to pass
608 // the format information and, basically, why bother -- just use
610 wxFAIL_MSG( wxT("UTF-8 text not supported in ANSI build") );
614 else // don't convert wxIPC_TEXT
616 realData
= (LPBYTE
)data
;
622 const wchar_t * const wtext
= (const wchar_t *)data
;
623 const size_t len
= size
/sizeof(wchar_t);
625 realSize
= conv
->FromWChar(NULL
, 0, wtext
, len
);
626 if ( realSize
== wxCONV_FAILED
)
629 realData
= (LPBYTE
)buffer
.GetWriteBuf(realSize
);
633 realSize
= conv
->FromWChar((char*)realData
, realSize
, wtext
, len
);
634 if ( realSize
== wxCONV_FAILED
)
637 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
640 bool ok
= DdeClientTransaction(realData
,
644 // MSDN: if the transaction specified by
645 // the wType parameter does not pass data
646 // or is XTYP_EXECUTE, wFmt should be zero.
654 DDELogError(wxT("DDE execute request failed"));
660 const void *wxDDEConnection::Request(const wxString
& item
, size_t *size
, wxIPCFormat format
)
664 HSZ atom
= DDEGetAtom(item
);
666 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
672 if ( !returned_data
)
674 DDELogError(wxT("DDE data request failed"));
679 DWORD len
= DdeGetData(returned_data
, NULL
, 0, 0);
681 void *data
= GetBufferAtLeast(len
);
682 wxASSERT_MSG(data
!= NULL
,
683 wxT("Buffer too small in wxDDEConnection::Request") );
684 (void) DdeGetData(returned_data
, (LPBYTE
)data
, len
, 0);
686 (void) DdeFreeDataHandle(returned_data
);
694 bool wxDDEConnection::DoPoke(const wxString
& item
, const void *data
, size_t size
, wxIPCFormat format
)
698 HSZ item_atom
= DDEGetAtom(item
);
699 bool ok
= DdeClientTransaction((LPBYTE
)data
,
708 DDELogError(_("DDE poke request failed"));
714 bool wxDDEConnection::StartAdvise(const wxString
& item
)
717 HSZ atom
= DDEGetAtom(item
);
719 bool ok
= DdeClientTransaction(NULL
, 0,
727 DDELogError(_("Failed to establish an advise loop with DDE server"));
733 bool wxDDEConnection::StopAdvise(const wxString
& item
)
736 HSZ atom
= DDEGetAtom(item
);
738 bool ok
= DdeClientTransaction(NULL
, 0,
746 DDELogError(_("Failed to terminate the advise loop with DDE server"));
752 // Calls that SERVER can make
753 bool wxDDEConnection::DoAdvise(const wxString
& item
,
758 HSZ item_atom
= DDEGetAtom(item
);
759 HSZ topic_atom
= DDEGetAtom(m_topicName
);
760 m_sendingData
= data
; // mrf: potential for scope problems here?
762 // wxIPC_PRIVATE does not succeed, so use text instead
763 m_dataType
= format
== wxIPC_PRIVATE
? wxIPC_TEXT
: format
;
765 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
768 DDELogError(_("Failed to send DDE advise notification"));
774 // ----------------------------------------------------------------------------
776 // ----------------------------------------------------------------------------
778 #define DDERETURN HDDEDATA
780 HDDEDATA EXPENTRY _EXPORT
781 _DDECallback(WORD wType
,
787 DWORD
WXUNUSED(lData1
),
788 DWORD
WXUNUSED(lData2
))
794 wxString topic
= DDEStringFromAtom(hsz1
),
795 srv
= DDEStringFromAtom(hsz2
);
796 wxDDEServer
*server
= DDEFindServer(srv
);
799 wxDDEConnection
*connection
=
800 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
803 connection
->m_server
= server
;
804 server
->GetConnections().Append(connection
);
805 connection
->m_hConv
= 0;
806 connection
->m_topicName
= topic
;
807 DDECurrentlyConnecting
= connection
;
808 return (DDERETURN
)(DWORD
)true;
814 case XTYP_CONNECT_CONFIRM
:
816 if (DDECurrentlyConnecting
)
818 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
819 DDECurrentlyConnecting
= NULL
;
820 return (DDERETURN
)(DWORD
)true;
825 case XTYP_DISCONNECT
:
827 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
830 connection
->SetConnected( false );
831 if (connection
->OnDisconnect())
833 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
834 return (DDERETURN
)(DWORD
)true;
842 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
846 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
848 void *data
= connection
->GetBufferAtLeast(len
);
849 wxASSERT_MSG(data
!= NULL
,
850 wxT("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
852 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
854 DdeFreeDataHandle(hData
);
856 // XTYP_EXECUTE can be used for text only and the text is
857 // always in ANSI format for ANSI build and Unicode format
860 wFmt
= wxIPC_UNICODETEXT
;
865 if ( connection
->OnExecute(connection
->m_topicName
,
870 return (DDERETURN
)(DWORD
)DDE_FACK
;
874 return (DDERETURN
)DDE_FNOTPROCESSED
;
879 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
883 wxString item_name
= DDEStringFromAtom(hsz2
);
885 size_t user_size
= wxNO_LEN
;
886 const void *data
= connection
->OnRequest(connection
->m_topicName
,
892 if (user_size
== wxNO_LEN
)
897 user_size
= strlen((const char*)data
) + 1; // includes final NUL
899 case wxIPC_UNICODETEXT
:
900 user_size
= (wcslen((const wchar_t*)data
) + 1) * sizeof(wchar_t); // includes final NUL
906 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
913 return (DDERETURN
)handle
;
921 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
925 wxString item_name
= DDEStringFromAtom(hsz2
);
927 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
929 void *data
= connection
->GetBufferAtLeast(len
);
930 wxASSERT_MSG(data
!= NULL
,
931 wxT("Buffer too small in _DDECallback (XTYP_POKE)") );
933 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
935 DdeFreeDataHandle(hData
);
937 connection
->OnPoke(connection
->m_topicName
,
943 return (DDERETURN
)DDE_FACK
;
947 return (DDERETURN
)DDE_FNOTPROCESSED
;
953 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
957 wxString item_name
= DDEStringFromAtom(hsz2
);
959 return (DDERETURN
)connection
->
960 OnStartAdvise(connection
->m_topicName
, item_name
);
968 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
972 wxString item_name
= DDEStringFromAtom(hsz2
);
974 return (DDERETURN
)connection
->
975 OnStopAdvise(connection
->m_topicName
, item_name
);
983 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
985 if (connection
&& connection
->m_sendingData
)
987 HDDEDATA data
= DdeCreateDataHandle
990 (LPBYTE
)connection
->m_sendingData
,
991 connection
->m_dataSize
,
994 connection
->m_dataType
,
998 connection
->m_sendingData
= NULL
;
1000 return (DDERETURN
)data
;
1008 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
1012 wxString item_name
= DDEStringFromAtom(hsz2
);
1014 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
1016 void *data
= connection
->GetBufferAtLeast(len
);
1017 wxASSERT_MSG(data
!= NULL
,
1018 wxT("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
1020 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
1022 DdeFreeDataHandle(hData
);
1023 if ( connection
->OnAdvise(connection
->m_topicName
,
1027 (wxIPCFormat
) wFmt
) )
1029 return (DDERETURN
)(DWORD
)DDE_FACK
;
1033 return (DDERETURN
)DDE_FNOTPROCESSED
;
1037 return (DDERETURN
)0;
1040 // ----------------------------------------------------------------------------
1041 // DDE strings and atoms
1042 // ----------------------------------------------------------------------------
1045 static HSZ
DDEAddAtom(const wxString
& str
)
1047 HSZ atom
= DDEAtomFromString(str
);
1048 wxAtomTable
[str
] = atom
;
1052 static HSZ
DDEGetAtom(const wxString
& str
)
1054 wxAtomMap::iterator it
= wxAtomTable
.find(str
);
1056 if (it
!= wxAtomTable
.end())
1059 return DDEAddAtom(str
);
1063 The returned handle has to be freed by the caller (using
1064 (static) DDEFreeString).
1066 static HSZ
DDEAtomFromString(const wxString
& s
)
1068 wxASSERT_MSG( DDEIdInst
, wxT("DDE not initialized") );
1070 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, wxMSW_CONV_LPTSTR(s
), DDE_CP
);
1073 DDELogError(_("Failed to create DDE string"));
1079 static wxString
DDEStringFromAtom(HSZ hsz
)
1081 // all DDE strings are normally limited to 255 bytes
1082 static const size_t len
= 256;
1085 (void)DdeQueryString(DDEIdInst
, hsz
, wxStringBuffer(s
, len
), len
, DDE_CP
);
1090 static void DDEFreeString(HSZ hsz
)
1092 // DS: Failure to free a string handle might indicate there's
1093 // some other severe error.
1094 bool ok
= (::DdeFreeStringHandle(DDEIdInst
, hsz
) != 0);
1095 wxASSERT_MSG( ok
, wxT("Failed to free DDE string handle") );
1099 // ----------------------------------------------------------------------------
1101 // ----------------------------------------------------------------------------
1103 static void DDELogError(const wxString
& s
, UINT error
)
1107 error
= DdeGetLastError(DDEIdInst
);
1110 wxLogError(s
+ wxT(": ") + DDEGetErrorMsg(error
));
1113 static wxString
DDEGetErrorMsg(UINT error
)
1118 case DMLERR_NO_ERROR
:
1119 err
= _("no DDE error.");
1122 case DMLERR_ADVACKTIMEOUT
:
1123 err
= _("a request for a synchronous advise transaction has timed out.");
1126 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
1128 case DMLERR_DATAACKTIMEOUT
:
1129 err
= _("a request for a synchronous data transaction has timed out.");
1131 case DMLERR_DLL_NOT_INITIALIZED
:
1132 err
= _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
1134 case DMLERR_DLL_USAGE
:
1135 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.");
1137 case DMLERR_EXECACKTIMEOUT
:
1138 err
= _("a request for a synchronous execute transaction has timed out.");
1140 case DMLERR_INVALIDPARAMETER
:
1141 err
= _("a parameter failed to be validated by the DDEML.");
1143 case DMLERR_LOW_MEMORY
:
1144 err
= _("a DDEML application has created a prolonged race condition.");
1146 case DMLERR_MEMORY_ERROR
:
1147 err
= _("a memory allocation failed.");
1149 case DMLERR_NO_CONV_ESTABLISHED
:
1150 err
= _("a client's attempt to establish a conversation has failed.");
1152 case DMLERR_NOTPROCESSED
:
1153 err
= _("a transaction failed.");
1155 case DMLERR_POKEACKTIMEOUT
:
1156 err
= _("a request for a synchronous poke transaction has timed out.");
1158 case DMLERR_POSTMSG_FAILED
:
1159 err
= _("an internal call to the PostMessage function has failed. ");
1161 case DMLERR_REENTRANCY
:
1162 err
= _("reentrancy problem.");
1164 case DMLERR_SERVER_DIED
:
1165 err
= _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1167 case DMLERR_SYS_ERROR
:
1168 err
= _("an internal error has occurred in the DDEML.");
1170 case DMLERR_UNADVACKTIMEOUT
:
1171 err
= _("a request to end an advise transaction has timed out.");
1173 case DMLERR_UNFOUND_QUEUE_ID
:
1174 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.");
1177 err
.Printf(_("Unknown DDE error %08x"), error
);