1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: DDE classes
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart and Markus Holzem
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"
50 #define PCONVCONTEXT CONVCONTEXT*
53 #if defined(__TWIN32__) || defined(__GNUWIN32_OLD__)
54 #include "wx/msw/gnuwin32/extra.h"
57 // some compilers headers don't define this one (mingw32)
58 #ifndef DMLERR_NO_ERROR
59 #define DMLERR_NO_ERROR (0)
61 // this one is also missing from some mingw32 headers, but there is no way
62 // to test for it (I know of) - the test for DMLERR_NO_ERROR works for me,
63 // but is surely not the right thing to do
65 HDDEDATA STDCALL
DdeClientTransaction(LPBYTE pData
,
73 #endif // no DMLERR_NO_ERROR
75 // ----------------------------------------------------------------------------
76 // macros and constants
77 // ----------------------------------------------------------------------------
82 #define _EXPORT _export
86 #define DDE_CP CP_WINUNICODE
88 #define DDE_CP CP_WINANSI
91 #define GetHConv() ((HCONV)m_hConv)
93 // default timeout for DDE operations (5sec)
94 #define DDE_TIMEOUT 5000
96 // ----------------------------------------------------------------------------
98 // ----------------------------------------------------------------------------
100 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
101 static void DDEDeleteConnection(HCONV hConv
);
102 static wxDDEServer
*DDEFindServer(const wxString
& s
);
104 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(WORD wType
,
113 // Add topic name to atom table before using in conversations
114 static HSZ
DDEAddAtom(const wxString
& string
);
115 static HSZ
DDEGetAtom(const wxString
& string
);
118 static HSZ
DDEAtomFromString(const wxString
& s
);
119 static wxString
DDEStringFromAtom(HSZ hsz
);
122 static wxString
DDEGetErrorMsg(UINT error
);
123 static void DDELogError(const wxString
& s
, UINT error
= DMLERR_NO_ERROR
);
125 // ----------------------------------------------------------------------------
127 // ----------------------------------------------------------------------------
129 static DWORD DDEIdInst
= 0L;
130 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
132 static wxList
wxAtomTable(wxKEY_STRING
);
133 static wxList wxDDEClientObjects
;
134 static wxList wxDDEServerObjects
;
136 static bool DDEInitialized
= FALSE
;
138 // ----------------------------------------------------------------------------
140 // ----------------------------------------------------------------------------
142 // A module to allow DDE cleanup without calling these functions
143 // from app.cpp or from the user's application.
145 class wxDDEModule
: public wxModule
149 bool OnInit() { return TRUE
; }
150 void OnExit() { wxDDECleanUp(); }
153 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
156 // ----------------------------------------------------------------------------
158 // ----------------------------------------------------------------------------
160 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
161 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
162 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
163 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
165 // ============================================================================
167 // ============================================================================
169 // ----------------------------------------------------------------------------
170 // initialization and cleanup
171 // ----------------------------------------------------------------------------
173 extern void wxDDEInitialize()
175 if ( !DDEInitialized
)
177 // Should insert filter flags
178 PFNCALLBACK callback
= (PFNCALLBACK
)
179 MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance());
180 UINT rc
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L);
181 if ( rc
!= DMLERR_NO_ERROR
)
183 DDELogError(_T("Failed to initialize DDE"), rc
);
187 DDEInitialized
= TRUE
;
194 wxDDEClientObjects
.DeleteContents(TRUE
);
195 wxDDEClientObjects
.Clear();
196 wxDDEClientObjects
.DeleteContents(FALSE
);
198 wxDDEServerObjects
.DeleteContents(TRUE
);
199 wxDDEServerObjects
.Clear();
200 wxDDEServerObjects
.DeleteContents(FALSE
);
204 if ( DDEIdInst
!= 0 )
206 DdeUninitialize(DDEIdInst
);
211 // ----------------------------------------------------------------------------
212 // functions working with the global connection list(s)
213 // ----------------------------------------------------------------------------
215 // Global find connection
216 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
218 wxNode
*node
= wxDDEServerObjects
.First();
219 wxDDEConnection
*found
= NULL
;
220 while (node
&& !found
)
222 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
223 found
= object
->FindConnection((WXHCONV
) hConv
);
229 node
= wxDDEClientObjects
.First();
230 while (node
&& !found
)
232 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
233 found
= object
->FindConnection((WXHCONV
) hConv
);
239 // Global delete connection
240 static void DDEDeleteConnection(HCONV hConv
)
242 wxNode
*node
= wxDDEServerObjects
.First();
244 while (node
&& !found
)
246 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
247 found
= object
->DeleteConnection((WXHCONV
) hConv
);
253 node
= wxDDEClientObjects
.First();
254 while (node
&& !found
)
256 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
257 found
= object
->DeleteConnection((WXHCONV
) hConv
);
262 // Find a server from a service name
263 static wxDDEServer
*DDEFindServer(const wxString
& s
)
265 wxNode
*node
= wxDDEServerObjects
.First();
266 wxDDEServer
*found
= NULL
;
267 while (node
&& !found
)
269 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
271 if (object
->GetServiceName() == s
)
273 else node
= node
->Next();
278 // ----------------------------------------------------------------------------
280 // ----------------------------------------------------------------------------
282 wxDDEServer::wxDDEServer()
286 wxDDEServerObjects
.Append(this);
289 bool wxDDEServer::Create(const wxString
& server
)
291 m_serviceName
= server
;
293 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(server
), (HSZ
)NULL
, DNS_REGISTER
) )
295 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
304 wxDDEServer::~wxDDEServer()
306 if ( !!m_serviceName
)
308 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(m_serviceName
),
309 (HSZ
)NULL
, DNS_UNREGISTER
) )
311 DDELogError(wxString::Format(_("Failed to unregister DDE server '%s'"),
312 m_serviceName
.c_str()));
316 wxDDEServerObjects
.DeleteObject(this);
318 wxNode
*node
= m_connections
.First();
321 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
322 wxNode
*next
= node
->Next();
323 connection
->SetConnected(false);
324 connection
->OnDisconnect(); // May delete the node implicitly
328 // If any left after this, delete them
329 node
= m_connections
.First();
332 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
333 wxNode
*next
= node
->Next();
339 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
341 return new wxDDEConnection
;
344 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
346 wxNode
*node
= m_connections
.First();
347 wxDDEConnection
*found
= NULL
;
348 while (node
&& !found
)
350 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
351 if (connection
->m_hConv
== conv
)
353 else node
= node
->Next();
358 // Only delete the entry in the map, not the actual connection
359 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
361 wxNode
*node
= m_connections
.First();
363 while (node
&& !found
)
365 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
366 if (connection
->m_hConv
== conv
)
371 else node
= node
->Next();
376 // ----------------------------------------------------------------------------
378 // ----------------------------------------------------------------------------
380 wxDDEClient::wxDDEClient()
384 wxDDEClientObjects
.Append(this);
387 wxDDEClient::~wxDDEClient()
389 wxDDEClientObjects
.DeleteObject(this);
390 wxNode
*node
= m_connections
.First();
393 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
394 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
395 node
= m_connections
.First();
399 bool wxDDEClient::ValidHost(const wxString
& /* host */)
404 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
),
405 const wxString
& server
,
406 const wxString
& topic
)
408 HCONV hConv
= DdeConnect(DDEIdInst
, DDEAtomFromString(server
), DDEAtomFromString(topic
),
412 DDELogError(wxString::Format(_("Failed to create connection to server '%s' on topic '%s'"),
413 server
.c_str(), topic
.c_str()));
417 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
420 connection
->m_hConv
= (WXHCONV
) hConv
;
421 connection
->m_topicName
= topic
;
422 connection
->m_client
= this;
423 m_connections
.Append(connection
);
428 return (wxConnectionBase
*) NULL
;
431 wxConnectionBase
*wxDDEClient::OnMakeConnection()
433 return new wxDDEConnection
;
436 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
438 wxNode
*node
= m_connections
.First();
439 wxDDEConnection
*found
= NULL
;
440 while (node
&& !found
)
442 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
443 if (connection
->m_hConv
== conv
)
445 else node
= node
->Next();
450 // Only delete the entry in the map, not the actual connection
451 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
453 wxNode
*node
= m_connections
.First();
455 while (node
&& !found
)
457 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
458 if (connection
->m_hConv
== conv
)
463 else node
= node
->Next();
468 // ----------------------------------------------------------------------------
470 // ----------------------------------------------------------------------------
472 wxDDEConnection::wxDDEConnection(wxChar
*buffer
, int size
)
473 : wxConnectionBase(buffer
, size
)
479 m_sendingData
= NULL
;
482 wxDDEConnection::wxDDEConnection()
486 m_sendingData
= NULL
;
491 wxDDEConnection::~wxDDEConnection()
495 m_server
->GetConnections().DeleteObject(this);
497 m_client
->GetConnections().DeleteObject(this);
500 // Calls that CLIENT can make
501 bool wxDDEConnection::Disconnect()
503 if ( !GetConnected() )
506 DDEDeleteConnection(GetHConv());
508 bool ok
= DdeDisconnect(GetHConv()) != 0;
511 DDELogError(_T("Failed to disconnect from DDE server gracefully"));
514 SetConnected( false ); // so we don't try and disconnect again
519 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
524 size
= wxStrlen(data
) + 1;
527 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
537 DDELogError(_T("DDE execute request failed"));
543 wxChar
*wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
547 HSZ atom
= DDEGetAtom(item
);
549 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
555 if ( !returned_data
)
557 DDELogError(_T("DDE data request failed"));
562 DWORD len
= DdeGetData(returned_data
, NULL
, 0, 0);
564 wxChar
*data
= GetBufferAtLeast( len
);
565 wxASSERT_MSG(data
!= NULL
,
566 _T("Buffer too small in wxDDEConnection::Request") );
567 DdeGetData(returned_data
, (LPBYTE
)data
, len
, 0);
569 DdeFreeDataHandle(returned_data
);
577 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
582 size
= wxStrlen(data
) + 1;
585 HSZ item_atom
= DDEGetAtom(item
);
586 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
594 DDELogError(_("DDE poke request failed"));
600 bool wxDDEConnection::StartAdvise(const wxString
& item
)
603 HSZ atom
= DDEGetAtom(item
);
605 bool ok
= DdeClientTransaction(NULL
, 0,
613 DDELogError(_("Failed to establish an advise loop with DDE server"));
619 bool wxDDEConnection::StopAdvise(const wxString
& item
)
622 HSZ atom
= DDEGetAtom(item
);
624 bool ok
= DdeClientTransaction(NULL
, 0,
632 DDELogError(_("Failed to terminate the advise loop with DDE server"));
638 // Calls that SERVER can make
639 bool wxDDEConnection::Advise(const wxString
& item
,
646 size
= wxStrlen(data
) + 1;
649 HSZ item_atom
= DDEGetAtom(item
);
650 HSZ topic_atom
= DDEGetAtom(m_topicName
);
651 m_sendingData
= data
; // mrf: potential for scope problems here?
655 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
658 DDELogError(_("Failed to send DDE advise notification"));
664 bool wxDDEConnection::OnDisconnect()
670 // ----------------------------------------------------------------------------
672 // ----------------------------------------------------------------------------
674 #define DDERETURN HDDEDATA
676 HDDEDATA EXPENTRY _EXPORT
677 _DDECallback(WORD wType
,
683 DWORD
WXUNUSED(lData1
),
684 DWORD
WXUNUSED(lData2
))
690 wxString topic
= DDEStringFromAtom(hsz1
),
691 srv
= DDEStringFromAtom(hsz2
);
692 wxDDEServer
*server
= DDEFindServer(srv
);
695 wxDDEConnection
*connection
=
696 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
699 connection
->m_server
= server
;
700 server
->GetConnections().Append(connection
);
701 connection
->m_hConv
= 0;
702 connection
->m_topicName
= topic
;
703 DDECurrentlyConnecting
= connection
;
704 return (DDERETURN
)(DWORD
)TRUE
;
710 case XTYP_CONNECT_CONFIRM
:
712 if (DDECurrentlyConnecting
)
714 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
715 DDECurrentlyConnecting
= NULL
;
716 return (DDERETURN
)(DWORD
)TRUE
;
721 case XTYP_DISCONNECT
:
723 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
726 connection
->SetConnected( false );
727 if (connection
->OnDisconnect())
729 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
730 return (DDERETURN
)(DWORD
)TRUE
;
738 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
742 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
744 wxChar
*data
= connection
->GetBufferAtLeast( len
);
745 wxASSERT_MSG(data
!= NULL
,
746 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
748 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
750 DdeFreeDataHandle(hData
);
752 if ( connection
->OnExecute(connection
->m_topicName
,
755 (wxIPCFormat
) wFmt
) )
757 return (DDERETURN
)(DWORD
)DDE_FACK
;
761 return (DDERETURN
)DDE_FNOTPROCESSED
;
766 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
770 wxString item_name
= DDEStringFromAtom(hsz2
);
773 wxChar
*data
= connection
->OnRequest(connection
->m_topicName
,
780 user_size
= wxStrlen((wxChar
*)data
) + 1;
782 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
789 return (DDERETURN
)handle
;
797 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
801 wxString item_name
= DDEStringFromAtom(hsz2
);
803 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
805 wxChar
*data
= connection
->GetBufferAtLeast( len
);
806 wxASSERT_MSG(data
!= NULL
,
807 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
809 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
811 DdeFreeDataHandle(hData
);
813 connection
->OnPoke(connection
->m_topicName
,
819 return (DDERETURN
)DDE_FACK
;
823 return (DDERETURN
)DDE_FNOTPROCESSED
;
829 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
833 wxString item_name
= DDEStringFromAtom(hsz2
);
835 return (DDERETURN
)connection
->
836 OnStartAdvise(connection
->m_topicName
, item_name
);
844 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
848 wxString item_name
= DDEStringFromAtom(hsz2
);
850 return (DDERETURN
)connection
->
851 OnStopAdvise(connection
->m_topicName
, item_name
);
859 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
861 if (connection
&& connection
->m_sendingData
)
863 HDDEDATA data
= DdeCreateDataHandle
866 (LPBYTE
)connection
->m_sendingData
,
867 connection
->m_dataSize
,
870 connection
->m_dataType
,
874 connection
->m_sendingData
= NULL
;
876 return (DDERETURN
)data
;
884 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
888 wxString item_name
= DDEStringFromAtom(hsz2
);
890 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
892 wxChar
*data
= connection
->GetBufferAtLeast( len
);
893 wxASSERT_MSG(data
!= NULL
,
894 _T("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
896 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
898 DdeFreeDataHandle(hData
);
899 if ( connection
->OnAdvise(connection
->m_topicName
,
903 (wxIPCFormat
) wFmt
) )
905 return (DDERETURN
)(DWORD
)DDE_FACK
;
909 return (DDERETURN
)DDE_FNOTPROCESSED
;
916 // ----------------------------------------------------------------------------
917 // DDE strings and atoms
918 // ----------------------------------------------------------------------------
921 static HSZ
DDEAddAtom(const wxString
& string
)
923 HSZ atom
= DDEAtomFromString(string
);
924 wxAtomTable
.Append(string
, (wxObject
*)atom
);
928 static HSZ
DDEGetAtom(const wxString
& string
)
930 wxNode
*node
= wxAtomTable
.Find(string
);
932 return (HSZ
)node
->Data();
936 return (HSZ
)(wxAtomTable
.Find(string
)->Data());
941 static HSZ
DDEAtomFromString(const wxString
& s
)
943 wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") );
945 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, (wxChar
*) s
.c_str(), DDE_CP
);
948 DDELogError(_("Failed to create DDE string"));
954 static wxString
DDEStringFromAtom(HSZ hsz
)
956 // all DDE strings are normally limited to 255 bytes
957 static const size_t len
= 256;
960 (void)DdeQueryString(DDEIdInst
, hsz
, s
.GetWriteBuf(len
), len
, DDE_CP
);
966 // ----------------------------------------------------------------------------
968 // ----------------------------------------------------------------------------
970 static void DDELogError(const wxString
& s
, UINT error
)
974 error
= DdeGetLastError(DDEIdInst
);
977 wxLogError(s
+ _T(": ") + DDEGetErrorMsg(error
));
980 static wxString
DDEGetErrorMsg(UINT error
)
985 case DMLERR_NO_ERROR
:
986 err
= _("no DDE error.");
989 case DMLERR_ADVACKTIMEOUT
:
990 err
= _("a request for a synchronous advise transaction has timed out.");
993 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
995 case DMLERR_DATAACKTIMEOUT
:
996 err
= _("a request for a synchronous data transaction has timed out.");
998 case DMLERR_DLL_NOT_INITIALIZED
:
999 err
= _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
1001 case DMLERR_DLL_USAGE
:
1002 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.");
1004 case DMLERR_EXECACKTIMEOUT
:
1005 err
= _("a request for a synchronous execute transaction has timed out.");
1007 case DMLERR_INVALIDPARAMETER
:
1008 err
= _("a parameter failed to be validated by the DDEML.");
1010 case DMLERR_LOW_MEMORY
:
1011 err
= _("a DDEML application has created a prolonged race condition.");
1013 case DMLERR_MEMORY_ERROR
:
1014 err
= _("a memory allocation failed.");
1016 case DMLERR_NO_CONV_ESTABLISHED
:
1017 err
= _("a client's attempt to establish a conversation has failed.");
1019 case DMLERR_NOTPROCESSED
:
1020 err
= _("a transaction failed.");
1022 case DMLERR_POKEACKTIMEOUT
:
1023 err
= _("a request for a synchronous poke transaction has timed out.");
1025 case DMLERR_POSTMSG_FAILED
:
1026 err
= _("an internal call to the PostMessage function has failed. ");
1028 case DMLERR_REENTRANCY
:
1029 err
= _("reentrancy problem.");
1031 case DMLERR_SERVER_DIED
:
1032 err
= _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1034 case DMLERR_SYS_ERROR
:
1035 err
= _("an internal error has occurred in the DDEML.");
1037 case DMLERR_UNADVACKTIMEOUT
:
1038 err
= _("a request to end an advise transaction has timed out.");
1040 case DMLERR_UNFOUND_QUEUE_ID
:
1041 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.");
1044 err
.Printf(_("Unknown DDE error %08x"), error
);