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"
43 #include "wx/msw/private.h"
49 #ifdef __GNUWIN32_OLD__
50 #include "wx/msw/gnuwin32/extra.h"
53 // some compilers headers don't define this one (mingw32)
54 #ifndef DMLERR_NO_ERROR
55 #define DMLERR_NO_ERROR (0)
57 // this one is also missing from some mingw32 headers, but there is no way
58 // to test for it (I know of) - the test for DMLERR_NO_ERROR works for me,
59 // but is surely not the right thing to do
61 HDDEDATA STDCALL
DdeClientTransaction(LPBYTE pData
,
69 #endif // no DMLERR_NO_ERROR
71 // ----------------------------------------------------------------------------
72 // macros and constants
73 // ----------------------------------------------------------------------------
78 #define _EXPORT _export
82 #define DDE_CP CP_WINUNICODE
84 #define DDE_CP CP_WINANSI
87 #define GetHConv() ((HCONV)m_hConv)
89 // default timeout for DDE operations (5sec)
90 #define DDE_TIMEOUT 5000
92 // ----------------------------------------------------------------------------
94 // ----------------------------------------------------------------------------
96 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
97 static void DDEDeleteConnection(HCONV hConv
);
98 static wxDDEServer
*DDEFindServer(const wxString
& s
);
100 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(WORD wType
,
109 // Add topic name to atom table before using in conversations
110 static HSZ
DDEAddAtom(const wxString
& string
);
111 static HSZ
DDEGetAtom(const wxString
& string
);
114 static HSZ
DDEAtomFromString(const wxString
& s
);
115 static wxString
DDEStringFromAtom(HSZ hsz
);
118 static wxString
DDEGetErrorMsg(UINT error
);
119 static void DDELogError(const wxString
& s
, UINT error
= DMLERR_NO_ERROR
);
121 // ----------------------------------------------------------------------------
123 // ----------------------------------------------------------------------------
125 static DWORD DDEIdInst
= 0L;
126 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
127 static wxList
wxAtomTable(wxKEY_STRING
);
129 #include "wx/listimpl.cpp"
131 WX_DEFINE_LIST(wxDDEClientList
);
132 WX_DEFINE_LIST(wxDDEServerList
);
133 WX_DEFINE_LIST(wxDDEConnectionList
);
135 static wxDDEClientList wxDDEClientObjects
;
136 static wxDDEServerList wxDDEServerObjects
;
138 static bool DDEInitialized
= false;
140 // ----------------------------------------------------------------------------
142 // ----------------------------------------------------------------------------
144 // A module to allow DDE cleanup without calling these functions
145 // from app.cpp or from the user's application.
147 class wxDDEModule
: public wxModule
151 bool OnInit() { return true; }
152 void OnExit() { wxDDECleanUp(); }
155 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
158 // ----------------------------------------------------------------------------
160 // ----------------------------------------------------------------------------
162 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
163 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
164 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
165 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
167 // ============================================================================
169 // ============================================================================
171 // ----------------------------------------------------------------------------
172 // initialization and cleanup
173 // ----------------------------------------------------------------------------
175 extern void wxDDEInitialize()
177 if ( !DDEInitialized
)
179 // Should insert filter flags
180 PFNCALLBACK callback
= (PFNCALLBACK
)
181 MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance());
182 UINT rc
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L);
183 if ( rc
!= DMLERR_NO_ERROR
)
185 DDELogError(_T("Failed to initialize DDE"), rc
);
189 DDEInitialized
= true;
196 wxDDEClientObjects
.DeleteContents(true);
197 wxDDEClientObjects
.Clear();
198 wxDDEClientObjects
.DeleteContents(false);
200 wxDDEServerObjects
.DeleteContents(true);
201 wxDDEServerObjects
.Clear();
202 wxDDEServerObjects
.DeleteContents(false);
206 if ( DDEIdInst
!= 0 )
208 DdeUninitialize(DDEIdInst
);
213 // ----------------------------------------------------------------------------
214 // functions working with the global connection list(s)
215 // ----------------------------------------------------------------------------
217 // Global find connection
218 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
220 wxDDEServerList::Node
*serverNode
= wxDDEServerObjects
.GetFirst();
221 wxDDEConnection
*found
= NULL
;
222 while (serverNode
&& !found
)
224 wxDDEServer
*object
= serverNode
->GetData();
225 found
= object
->FindConnection((WXHCONV
) hConv
);
226 serverNode
= serverNode
->GetNext();
234 wxDDEClientList::Node
*clientNode
= wxDDEClientObjects
.GetFirst();
235 while (clientNode
&& !found
)
237 wxDDEClient
*object
= clientNode
->GetData();
238 found
= object
->FindConnection((WXHCONV
) hConv
);
239 clientNode
= clientNode
->GetNext();
244 // Global delete connection
245 static void DDEDeleteConnection(HCONV hConv
)
247 wxDDEServerList::Node
*serverNode
= wxDDEServerObjects
.GetFirst();
249 while (serverNode
&& !found
)
251 wxDDEServer
*object
= serverNode
->GetData();
252 found
= object
->DeleteConnection((WXHCONV
) hConv
);
253 serverNode
= serverNode
->GetNext();
260 wxDDEClientList::Node
*clientNode
= wxDDEClientObjects
.GetFirst();
261 while (clientNode
&& !found
)
263 wxDDEClient
*object
= clientNode
->GetData();
264 found
= object
->DeleteConnection((WXHCONV
) hConv
);
265 clientNode
= clientNode
->GetNext();
269 // Find a server from a service name
270 static wxDDEServer
*DDEFindServer(const wxString
& s
)
272 wxDDEServerList::Node
*node
= wxDDEServerObjects
.GetFirst();
273 wxDDEServer
*found
= NULL
;
274 while (node
&& !found
)
276 wxDDEServer
*object
= node
->GetData();
278 if (object
->GetServiceName() == s
)
284 node
= node
->GetNext();
291 // ----------------------------------------------------------------------------
293 // ----------------------------------------------------------------------------
295 wxDDEServer::wxDDEServer()
299 wxDDEServerObjects
.Append(this);
302 bool wxDDEServer::Create(const wxString
& server
)
304 m_serviceName
= server
;
306 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(server
), (HSZ
)NULL
, DNS_REGISTER
) )
308 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
317 wxDDEServer::~wxDDEServer()
319 if ( !!m_serviceName
)
321 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(m_serviceName
),
322 (HSZ
)NULL
, DNS_UNREGISTER
) )
324 DDELogError(wxString::Format(_("Failed to unregister DDE server '%s'"),
325 m_serviceName
.c_str()));
329 wxDDEServerObjects
.DeleteObject(this);
331 wxDDEConnectionList::Node
*node
= m_connections
.GetFirst();
334 wxDDEConnection
*connection
= node
->GetData();
335 wxDDEConnectionList::Node
*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::Node
*next
= node
->GetNext();
352 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
354 return new wxDDEConnection
;
357 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
359 wxDDEConnectionList::Node
*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::Node
*node
= m_connections
.GetFirst();
376 while (node
&& !found
)
378 wxDDEConnection
*connection
= node
->GetData();
379 if (connection
->m_hConv
== conv
)
386 node
= node
->GetNext();
392 // ----------------------------------------------------------------------------
394 // ----------------------------------------------------------------------------
396 wxDDEClient::wxDDEClient()
400 wxDDEClientObjects
.Append(this);
403 wxDDEClient::~wxDDEClient()
405 wxDDEClientObjects
.DeleteObject(this);
406 wxDDEConnectionList::Node
*node
= m_connections
.GetFirst();
409 wxDDEConnection
*connection
= node
->GetData();
410 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
411 node
= m_connections
.GetFirst();
415 bool wxDDEClient::ValidHost(const wxString
& /* host */)
420 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
),
421 const wxString
& server
,
422 const wxString
& topic
)
424 HCONV hConv
= DdeConnect(DDEIdInst
, DDEAtomFromString(server
), DDEAtomFromString(topic
),
428 DDELogError(wxString::Format(_("Failed to create connection to server '%s' on topic '%s'"),
429 server
.c_str(), topic
.c_str()));
433 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
436 connection
->m_hConv
= (WXHCONV
) hConv
;
437 connection
->m_topicName
= topic
;
438 connection
->m_client
= this;
439 m_connections
.Append(connection
);
444 return (wxConnectionBase
*) NULL
;
447 wxConnectionBase
*wxDDEClient::OnMakeConnection()
449 return new wxDDEConnection
;
452 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
454 wxDDEConnectionList::Node
*node
= m_connections
.GetFirst();
455 wxDDEConnection
*found
= NULL
;
456 while (node
&& !found
)
458 wxDDEConnection
*connection
= node
->GetData();
459 if (connection
->m_hConv
== conv
)
461 else node
= node
->GetNext();
466 // Only delete the entry in the map, not the actual connection
467 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
469 wxDDEConnectionList::Node
*node
= m_connections
.GetFirst();
471 while (node
&& !found
)
473 wxDDEConnection
*connection
= node
->GetData();
474 if (connection
->m_hConv
== conv
)
479 else node
= node
->GetNext();
484 // ----------------------------------------------------------------------------
486 // ----------------------------------------------------------------------------
488 wxDDEConnection::wxDDEConnection(wxChar
*buffer
, int size
)
489 : wxConnectionBase(buffer
, size
)
495 m_sendingData
= NULL
;
498 wxDDEConnection::wxDDEConnection()
502 m_sendingData
= NULL
;
507 wxDDEConnection::~wxDDEConnection()
511 m_server
->GetConnections().DeleteObject(this);
513 m_client
->GetConnections().DeleteObject(this);
516 // Calls that CLIENT can make
517 bool wxDDEConnection::Disconnect()
519 if ( !GetConnected() )
522 DDEDeleteConnection(GetHConv());
524 bool ok
= DdeDisconnect(GetHConv()) != 0;
527 DDELogError(_T("Failed to disconnect from DDE server gracefully"));
530 SetConnected( false ); // so we don't try and disconnect again
535 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
540 size
= wxStrlen(data
) + 1;
543 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
553 DDELogError(_T("DDE execute request failed"));
559 wxChar
*wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
563 HSZ atom
= DDEGetAtom(item
);
565 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
571 if ( !returned_data
)
573 DDELogError(_T("DDE data request failed"));
578 DWORD len
= DdeGetData(returned_data
, NULL
, 0, 0);
580 wxChar
*data
= GetBufferAtLeast( len
);
581 wxASSERT_MSG(data
!= NULL
,
582 _T("Buffer too small in wxDDEConnection::Request") );
583 DdeGetData(returned_data
, (LPBYTE
)data
, len
, 0);
585 DdeFreeDataHandle(returned_data
);
593 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
598 size
= wxStrlen(data
) + 1;
601 HSZ item_atom
= DDEGetAtom(item
);
602 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
610 DDELogError(_("DDE poke request failed"));
616 bool wxDDEConnection::StartAdvise(const wxString
& item
)
619 HSZ atom
= DDEGetAtom(item
);
621 bool ok
= DdeClientTransaction(NULL
, 0,
629 DDELogError(_("Failed to establish an advise loop with DDE server"));
635 bool wxDDEConnection::StopAdvise(const wxString
& item
)
638 HSZ atom
= DDEGetAtom(item
);
640 bool ok
= DdeClientTransaction(NULL
, 0,
648 DDELogError(_("Failed to terminate the advise loop with DDE server"));
654 // Calls that SERVER can make
655 bool wxDDEConnection::Advise(const wxString
& item
,
662 size
= wxStrlen(data
) + 1;
665 HSZ item_atom
= DDEGetAtom(item
);
666 HSZ topic_atom
= DDEGetAtom(m_topicName
);
667 m_sendingData
= data
; // mrf: potential for scope problems here?
671 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
674 DDELogError(_("Failed to send DDE advise notification"));
680 bool wxDDEConnection::OnDisconnect()
686 // ----------------------------------------------------------------------------
688 // ----------------------------------------------------------------------------
690 #define DDERETURN HDDEDATA
692 HDDEDATA EXPENTRY _EXPORT
693 _DDECallback(WORD wType
,
699 DWORD
WXUNUSED(lData1
),
700 DWORD
WXUNUSED(lData2
))
706 wxString topic
= DDEStringFromAtom(hsz1
),
707 srv
= DDEStringFromAtom(hsz2
);
708 wxDDEServer
*server
= DDEFindServer(srv
);
711 wxDDEConnection
*connection
=
712 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
715 connection
->m_server
= server
;
716 server
->GetConnections().Append(connection
);
717 connection
->m_hConv
= 0;
718 connection
->m_topicName
= topic
;
719 DDECurrentlyConnecting
= connection
;
720 return (DDERETURN
)(DWORD
)true;
726 case XTYP_CONNECT_CONFIRM
:
728 if (DDECurrentlyConnecting
)
730 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
731 DDECurrentlyConnecting
= NULL
;
732 return (DDERETURN
)(DWORD
)true;
737 case XTYP_DISCONNECT
:
739 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
742 connection
->SetConnected( false );
743 if (connection
->OnDisconnect())
745 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
746 return (DDERETURN
)(DWORD
)true;
754 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
758 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
760 wxChar
*data
= connection
->GetBufferAtLeast( len
);
761 wxASSERT_MSG(data
!= NULL
,
762 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
764 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
766 DdeFreeDataHandle(hData
);
768 if ( connection
->OnExecute(connection
->m_topicName
,
771 (wxIPCFormat
) wFmt
) )
773 return (DDERETURN
)(DWORD
)DDE_FACK
;
777 return (DDERETURN
)DDE_FNOTPROCESSED
;
782 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
786 wxString item_name
= DDEStringFromAtom(hsz2
);
789 wxChar
*data
= connection
->OnRequest(connection
->m_topicName
,
796 user_size
= wxStrlen((wxChar
*)data
) + 1;
798 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
805 return (DDERETURN
)handle
;
813 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
817 wxString item_name
= DDEStringFromAtom(hsz2
);
819 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
821 wxChar
*data
= connection
->GetBufferAtLeast( len
);
822 wxASSERT_MSG(data
!= NULL
,
823 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
825 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
827 DdeFreeDataHandle(hData
);
829 connection
->OnPoke(connection
->m_topicName
,
835 return (DDERETURN
)DDE_FACK
;
839 return (DDERETURN
)DDE_FNOTPROCESSED
;
845 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
849 wxString item_name
= DDEStringFromAtom(hsz2
);
851 return (DDERETURN
)connection
->
852 OnStartAdvise(connection
->m_topicName
, item_name
);
860 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
864 wxString item_name
= DDEStringFromAtom(hsz2
);
866 return (DDERETURN
)connection
->
867 OnStopAdvise(connection
->m_topicName
, item_name
);
875 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
877 if (connection
&& connection
->m_sendingData
)
879 HDDEDATA data
= DdeCreateDataHandle
882 (LPBYTE
)connection
->m_sendingData
,
883 connection
->m_dataSize
,
886 connection
->m_dataType
,
890 connection
->m_sendingData
= NULL
;
892 return (DDERETURN
)data
;
900 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
904 wxString item_name
= DDEStringFromAtom(hsz2
);
906 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
908 wxChar
*data
= connection
->GetBufferAtLeast( len
);
909 wxASSERT_MSG(data
!= NULL
,
910 _T("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
912 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
914 DdeFreeDataHandle(hData
);
915 if ( connection
->OnAdvise(connection
->m_topicName
,
919 (wxIPCFormat
) wFmt
) )
921 return (DDERETURN
)(DWORD
)DDE_FACK
;
925 return (DDERETURN
)DDE_FNOTPROCESSED
;
932 // ----------------------------------------------------------------------------
933 // DDE strings and atoms
934 // ----------------------------------------------------------------------------
937 static HSZ
DDEAddAtom(const wxString
& string
)
939 HSZ atom
= DDEAtomFromString(string
);
940 wxAtomTable
.Append(string
, (wxObject
*)atom
);
944 static HSZ
DDEGetAtom(const wxString
& string
)
946 wxNode
*node
= wxAtomTable
.Find(string
);
948 return (HSZ
)node
->GetData();
952 return (HSZ
)(wxAtomTable
.Find(string
)->GetData());
957 static HSZ
DDEAtomFromString(const wxString
& s
)
959 wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") );
961 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, (wxChar
*) s
.c_str(), DDE_CP
);
964 DDELogError(_("Failed to create DDE string"));
970 static wxString
DDEStringFromAtom(HSZ hsz
)
972 // all DDE strings are normally limited to 255 bytes
973 static const size_t len
= 256;
976 (void)DdeQueryString(DDEIdInst
, hsz
, s
.GetWriteBuf(len
), len
, DDE_CP
);
982 // ----------------------------------------------------------------------------
984 // ----------------------------------------------------------------------------
986 static void DDELogError(const wxString
& s
, UINT error
)
990 error
= DdeGetLastError(DDEIdInst
);
993 wxLogError(s
+ _T(": ") + DDEGetErrorMsg(error
));
996 static wxString
DDEGetErrorMsg(UINT error
)
1001 case DMLERR_NO_ERROR
:
1002 err
= _("no DDE error.");
1005 case DMLERR_ADVACKTIMEOUT
:
1006 err
= _("a request for a synchronous advise transaction has timed out.");
1009 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
1011 case DMLERR_DATAACKTIMEOUT
:
1012 err
= _("a request for a synchronous data transaction has timed out.");
1014 case DMLERR_DLL_NOT_INITIALIZED
:
1015 err
= _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
1017 case DMLERR_DLL_USAGE
:
1018 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.");
1020 case DMLERR_EXECACKTIMEOUT
:
1021 err
= _("a request for a synchronous execute transaction has timed out.");
1023 case DMLERR_INVALIDPARAMETER
:
1024 err
= _("a parameter failed to be validated by the DDEML.");
1026 case DMLERR_LOW_MEMORY
:
1027 err
= _("a DDEML application has created a prolonged race condition.");
1029 case DMLERR_MEMORY_ERROR
:
1030 err
= _("a memory allocation failed.");
1032 case DMLERR_NO_CONV_ESTABLISHED
:
1033 err
= _("a client's attempt to establish a conversation has failed.");
1035 case DMLERR_NOTPROCESSED
:
1036 err
= _("a transaction failed.");
1038 case DMLERR_POKEACKTIMEOUT
:
1039 err
= _("a request for a synchronous poke transaction has timed out.");
1041 case DMLERR_POSTMSG_FAILED
:
1042 err
= _("an internal call to the PostMessage function has failed. ");
1044 case DMLERR_REENTRANCY
:
1045 err
= _("reentrancy problem.");
1047 case DMLERR_SERVER_DIED
:
1048 err
= _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1050 case DMLERR_SYS_ERROR
:
1051 err
= _("an internal error has occurred in the DDEML.");
1053 case DMLERR_UNADVACKTIMEOUT
:
1054 err
= _("a request to end an advise transaction has timed out.");
1056 case DMLERR_UNFOUND_QUEUE_ID
:
1057 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.");
1060 err
.Printf(_("Unknown DDE error %08x"), error
);