1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: DDE classes
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
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
);
117 static wxString
DDEGetErrorMsg(UINT error
);
118 static void DDELogError(const wxString
& s
, UINT error
= DMLERR_NO_ERROR
);
120 // ----------------------------------------------------------------------------
122 // ----------------------------------------------------------------------------
124 WX_DECLARE_STRING_HASH_MAP( HSZ
, wxAtomMap
);
126 static DWORD DDEIdInst
= 0L;
127 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
128 static wxAtomMap wxAtomTable
;
130 #include "wx/listimpl.cpp"
132 WX_DEFINE_LIST(wxDDEClientList
);
133 WX_DEFINE_LIST(wxDDEServerList
);
134 WX_DEFINE_LIST(wxDDEConnectionList
);
136 static wxDDEClientList wxDDEClientObjects
;
137 static wxDDEServerList wxDDEServerObjects
;
139 static bool DDEInitialized
= false;
141 // ----------------------------------------------------------------------------
143 // ----------------------------------------------------------------------------
145 // A module to allow DDE cleanup without calling these functions
146 // from app.cpp or from the user's application.
148 class wxDDEModule
: public wxModule
152 bool OnInit() { return true; }
153 void OnExit() { wxDDECleanUp(); }
156 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
159 // ----------------------------------------------------------------------------
161 // ----------------------------------------------------------------------------
163 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
164 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
165 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
166 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
168 // ============================================================================
170 // ============================================================================
172 // ----------------------------------------------------------------------------
173 // initialization and cleanup
174 // ----------------------------------------------------------------------------
176 extern void wxDDEInitialize()
178 if ( !DDEInitialized
)
180 // Should insert filter flags
181 PFNCALLBACK callback
= (PFNCALLBACK
)
182 MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance());
183 UINT rc
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L);
184 if ( rc
!= DMLERR_NO_ERROR
)
186 DDELogError(_T("Failed to initialize DDE"), rc
);
190 DDEInitialized
= true;
197 WX_CLEAR_LIST(wxDDEClientList
, wxDDEClientObjects
);
198 WX_CLEAR_LIST(wxDDEServerList
, wxDDEServerObjects
);
202 if ( DDEIdInst
!= 0 )
204 DdeUninitialize(DDEIdInst
);
209 // ----------------------------------------------------------------------------
210 // functions working with the global connection list(s)
211 // ----------------------------------------------------------------------------
213 // Global find connection
214 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
216 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
217 wxDDEConnection
*found
= NULL
;
218 while (serverNode
&& !found
)
220 wxDDEServer
*object
= serverNode
->GetData();
221 found
= object
->FindConnection((WXHCONV
) hConv
);
222 serverNode
= serverNode
->GetNext();
230 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
231 while (clientNode
&& !found
)
233 wxDDEClient
*object
= clientNode
->GetData();
234 found
= object
->FindConnection((WXHCONV
) hConv
);
235 clientNode
= clientNode
->GetNext();
240 // Global delete connection
241 static void DDEDeleteConnection(HCONV hConv
)
243 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
245 while (serverNode
&& !found
)
247 wxDDEServer
*object
= serverNode
->GetData();
248 found
= object
->DeleteConnection((WXHCONV
) hConv
);
249 serverNode
= serverNode
->GetNext();
256 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
257 while (clientNode
&& !found
)
259 wxDDEClient
*object
= clientNode
->GetData();
260 found
= object
->DeleteConnection((WXHCONV
) hConv
);
261 clientNode
= clientNode
->GetNext();
265 // Find a server from a service name
266 static wxDDEServer
*DDEFindServer(const wxString
& s
)
268 wxDDEServerList::compatibility_iterator node
= wxDDEServerObjects
.GetFirst();
269 wxDDEServer
*found
= NULL
;
270 while (node
&& !found
)
272 wxDDEServer
*object
= node
->GetData();
274 if (object
->GetServiceName() == s
)
280 node
= node
->GetNext();
287 // ----------------------------------------------------------------------------
289 // ----------------------------------------------------------------------------
291 wxDDEServer::wxDDEServer()
295 wxDDEServerObjects
.Append(this);
298 bool wxDDEServer::Create(const wxString
& server
)
300 m_serviceName
= server
;
302 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(server
), (HSZ
)NULL
, DNS_REGISTER
) )
304 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
313 wxDDEServer::~wxDDEServer()
315 if ( !!m_serviceName
)
317 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(m_serviceName
),
318 (HSZ
)NULL
, DNS_UNREGISTER
) )
320 DDELogError(wxString::Format(_("Failed to unregister DDE server '%s'"),
321 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 HCONV hConv
= DdeConnect(DDEIdInst
, DDEAtomFromString(server
), DDEAtomFromString(topic
),
423 DDELogError(wxString::Format(_("Failed to create connection to server '%s' on topic '%s'"),
424 server
.c_str(), topic
.c_str()));
428 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
431 connection
->m_hConv
= (WXHCONV
) hConv
;
432 connection
->m_topicName
= topic
;
433 connection
->m_client
= this;
434 m_connections
.Append(connection
);
439 return (wxConnectionBase
*) NULL
;
442 wxConnectionBase
*wxDDEClient::OnMakeConnection()
444 return new wxDDEConnection
;
447 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
449 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
450 wxDDEConnection
*found
= NULL
;
451 while (node
&& !found
)
453 wxDDEConnection
*connection
= node
->GetData();
454 if (connection
->m_hConv
== conv
)
456 else node
= node
->GetNext();
461 // Only delete the entry in the map, not the actual connection
462 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
464 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
467 wxDDEConnection
*connection
= node
->GetData();
468 if (connection
->m_hConv
== conv
)
470 m_connections
.Erase(node
);
473 else node
= node
->GetNext();
478 // ----------------------------------------------------------------------------
480 // ----------------------------------------------------------------------------
482 wxDDEConnection::wxDDEConnection(wxChar
*buffer
, int size
)
483 : wxConnectionBase(buffer
, size
)
489 m_sendingData
= NULL
;
492 wxDDEConnection::wxDDEConnection()
496 m_sendingData
= NULL
;
501 wxDDEConnection::~wxDDEConnection()
505 m_server
->GetConnections().DeleteObject(this);
507 m_client
->GetConnections().DeleteObject(this);
510 // Calls that CLIENT can make
511 bool wxDDEConnection::Disconnect()
513 if ( !GetConnected() )
516 DDEDeleteConnection(GetHConv());
518 bool ok
= DdeDisconnect(GetHConv()) != 0;
521 DDELogError(_T("Failed to disconnect from DDE server gracefully"));
524 SetConnected( false ); // so we don't try and disconnect again
529 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
534 size
= wxStrlen(data
) + 1;
537 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
547 DDELogError(_T("DDE execute request failed"));
553 wxChar
*wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
557 HSZ atom
= DDEGetAtom(item
);
559 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
565 if ( !returned_data
)
567 DDELogError(_T("DDE data request failed"));
572 DWORD len
= DdeGetData(returned_data
, NULL
, 0, 0);
574 wxChar
*data
= GetBufferAtLeast( len
);
575 wxASSERT_MSG(data
!= NULL
,
576 _T("Buffer too small in wxDDEConnection::Request") );
577 DdeGetData(returned_data
, (LPBYTE
)data
, len
, 0);
579 DdeFreeDataHandle(returned_data
);
587 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
592 size
= wxStrlen(data
) + 1;
595 HSZ item_atom
= DDEGetAtom(item
);
596 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
604 DDELogError(_("DDE poke request failed"));
610 bool wxDDEConnection::StartAdvise(const wxString
& item
)
613 HSZ atom
= DDEGetAtom(item
);
615 bool ok
= DdeClientTransaction(NULL
, 0,
623 DDELogError(_("Failed to establish an advise loop with DDE server"));
629 bool wxDDEConnection::StopAdvise(const wxString
& item
)
632 HSZ atom
= DDEGetAtom(item
);
634 bool ok
= DdeClientTransaction(NULL
, 0,
642 DDELogError(_("Failed to terminate the advise loop with DDE server"));
648 // Calls that SERVER can make
649 bool wxDDEConnection::Advise(const wxString
& item
,
656 size
= wxStrlen(data
) + 1;
659 HSZ item_atom
= DDEGetAtom(item
);
660 HSZ topic_atom
= DDEGetAtom(m_topicName
);
661 m_sendingData
= data
; // mrf: potential for scope problems here?
665 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
668 DDELogError(_("Failed to send DDE advise notification"));
674 bool wxDDEConnection::OnDisconnect()
680 // ----------------------------------------------------------------------------
682 // ----------------------------------------------------------------------------
684 #define DDERETURN HDDEDATA
686 HDDEDATA EXPENTRY _EXPORT
687 _DDECallback(WORD wType
,
693 DWORD
WXUNUSED(lData1
),
694 DWORD
WXUNUSED(lData2
))
700 wxString topic
= DDEStringFromAtom(hsz1
),
701 srv
= DDEStringFromAtom(hsz2
);
702 wxDDEServer
*server
= DDEFindServer(srv
);
705 wxDDEConnection
*connection
=
706 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
709 connection
->m_server
= server
;
710 server
->GetConnections().Append(connection
);
711 connection
->m_hConv
= 0;
712 connection
->m_topicName
= topic
;
713 DDECurrentlyConnecting
= connection
;
714 return (DDERETURN
)(DWORD
)true;
720 case XTYP_CONNECT_CONFIRM
:
722 if (DDECurrentlyConnecting
)
724 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
725 DDECurrentlyConnecting
= NULL
;
726 return (DDERETURN
)(DWORD
)true;
731 case XTYP_DISCONNECT
:
733 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
736 connection
->SetConnected( false );
737 if (connection
->OnDisconnect())
739 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
740 return (DDERETURN
)(DWORD
)true;
748 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
752 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
754 wxChar
*data
= connection
->GetBufferAtLeast( len
);
755 wxASSERT_MSG(data
!= NULL
,
756 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
758 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
760 DdeFreeDataHandle(hData
);
762 if ( connection
->OnExecute(connection
->m_topicName
,
765 (wxIPCFormat
) wFmt
) )
767 return (DDERETURN
)(DWORD
)DDE_FACK
;
771 return (DDERETURN
)DDE_FNOTPROCESSED
;
776 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
780 wxString item_name
= DDEStringFromAtom(hsz2
);
783 wxChar
*data
= connection
->OnRequest(connection
->m_topicName
,
790 user_size
= wxStrlen((wxChar
*)data
) + 1;
792 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
799 return (DDERETURN
)handle
;
807 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
811 wxString item_name
= DDEStringFromAtom(hsz2
);
813 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
815 wxChar
*data
= connection
->GetBufferAtLeast( len
);
816 wxASSERT_MSG(data
!= NULL
,
817 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
819 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
821 DdeFreeDataHandle(hData
);
823 connection
->OnPoke(connection
->m_topicName
,
829 return (DDERETURN
)DDE_FACK
;
833 return (DDERETURN
)DDE_FNOTPROCESSED
;
839 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
843 wxString item_name
= DDEStringFromAtom(hsz2
);
845 return (DDERETURN
)connection
->
846 OnStartAdvise(connection
->m_topicName
, item_name
);
854 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
858 wxString item_name
= DDEStringFromAtom(hsz2
);
860 return (DDERETURN
)connection
->
861 OnStopAdvise(connection
->m_topicName
, item_name
);
869 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
871 if (connection
&& connection
->m_sendingData
)
873 HDDEDATA data
= DdeCreateDataHandle
876 (LPBYTE
)connection
->m_sendingData
,
877 connection
->m_dataSize
,
880 connection
->m_dataType
,
884 connection
->m_sendingData
= NULL
;
886 return (DDERETURN
)data
;
894 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
898 wxString item_name
= DDEStringFromAtom(hsz2
);
900 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
902 wxChar
*data
= connection
->GetBufferAtLeast( len
);
903 wxASSERT_MSG(data
!= NULL
,
904 _T("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
906 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
908 DdeFreeDataHandle(hData
);
909 if ( connection
->OnAdvise(connection
->m_topicName
,
913 (wxIPCFormat
) wFmt
) )
915 return (DDERETURN
)(DWORD
)DDE_FACK
;
919 return (DDERETURN
)DDE_FNOTPROCESSED
;
926 // ----------------------------------------------------------------------------
927 // DDE strings and atoms
928 // ----------------------------------------------------------------------------
931 static HSZ
DDEAddAtom(const wxString
& str
)
933 HSZ atom
= DDEAtomFromString(str
);
934 wxAtomTable
[str
] = atom
;
938 static HSZ
DDEGetAtom(const wxString
& str
)
940 wxAtomMap::iterator it
= wxAtomTable
.find(str
);
942 if (it
!= wxAtomTable
.end())
945 return DDEAddAtom(str
);
949 static HSZ
DDEAtomFromString(const wxString
& s
)
951 wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") );
953 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, (wxChar
*) s
.c_str(), DDE_CP
);
956 DDELogError(_("Failed to create DDE string"));
962 static wxString
DDEStringFromAtom(HSZ hsz
)
964 // all DDE strings are normally limited to 255 bytes
965 static const size_t len
= 256;
968 (void)DdeQueryString(DDEIdInst
, hsz
, s
.GetWriteBuf(len
), len
, DDE_CP
);
974 // ----------------------------------------------------------------------------
976 // ----------------------------------------------------------------------------
978 static void DDELogError(const wxString
& s
, UINT error
)
982 error
= DdeGetLastError(DDEIdInst
);
985 wxLogError(s
+ _T(": ") + DDEGetErrorMsg(error
));
988 static wxString
DDEGetErrorMsg(UINT error
)
993 case DMLERR_NO_ERROR
:
994 err
= _("no DDE error.");
997 case DMLERR_ADVACKTIMEOUT
:
998 err
= _("a request for a synchronous advise transaction has timed out.");
1001 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
1003 case DMLERR_DATAACKTIMEOUT
:
1004 err
= _("a request for a synchronous data transaction has timed out.");
1006 case DMLERR_DLL_NOT_INITIALIZED
:
1007 err
= _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
1009 case DMLERR_DLL_USAGE
:
1010 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.");
1012 case DMLERR_EXECACKTIMEOUT
:
1013 err
= _("a request for a synchronous execute transaction has timed out.");
1015 case DMLERR_INVALIDPARAMETER
:
1016 err
= _("a parameter failed to be validated by the DDEML.");
1018 case DMLERR_LOW_MEMORY
:
1019 err
= _("a DDEML application has created a prolonged race condition.");
1021 case DMLERR_MEMORY_ERROR
:
1022 err
= _("a memory allocation failed.");
1024 case DMLERR_NO_CONV_ESTABLISHED
:
1025 err
= _("a client's attempt to establish a conversation has failed.");
1027 case DMLERR_NOTPROCESSED
:
1028 err
= _("a transaction failed.");
1030 case DMLERR_POKEACKTIMEOUT
:
1031 err
= _("a request for a synchronous poke transaction has timed out.");
1033 case DMLERR_POSTMSG_FAILED
:
1034 err
= _("an internal call to the PostMessage function has failed. ");
1036 case DMLERR_REENTRANCY
:
1037 err
= _("reentrancy problem.");
1039 case DMLERR_SERVER_DIED
:
1040 err
= _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1042 case DMLERR_SYS_ERROR
:
1043 err
= _("an internal error has occurred in the DDEML.");
1045 case DMLERR_UNADVACKTIMEOUT
:
1046 err
= _("a request to end an advise transaction has timed out.");
1048 case DMLERR_UNFOUND_QUEUE_ID
:
1049 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.");
1052 err
.Printf(_("Unknown DDE error %08x"), error
);