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 char *DDEDefaultIPCBuffer
= NULL
;
137 int DDEDefaultIPCBufferSize
= 0;
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 if ( DDEIdInst
!= 0 )
198 DdeUninitialize(DDEIdInst
);
202 delete [] DDEDefaultIPCBuffer
;
205 // ----------------------------------------------------------------------------
206 // functions working with the global connection list(s)
207 // ----------------------------------------------------------------------------
209 // Global find connection
210 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
212 wxNode
*node
= wxDDEServerObjects
.First();
213 wxDDEConnection
*found
= NULL
;
214 while (node
&& !found
)
216 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
217 found
= object
->FindConnection((WXHCONV
) hConv
);
223 node
= wxDDEClientObjects
.First();
224 while (node
&& !found
)
226 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
227 found
= object
->FindConnection((WXHCONV
) hConv
);
233 // Global delete connection
234 static void DDEDeleteConnection(HCONV hConv
)
236 wxNode
*node
= wxDDEServerObjects
.First();
238 while (node
&& !found
)
240 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
241 found
= object
->DeleteConnection((WXHCONV
) hConv
);
247 node
= wxDDEClientObjects
.First();
248 while (node
&& !found
)
250 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
251 found
= object
->DeleteConnection((WXHCONV
) hConv
);
256 // Find a server from a service name
257 static wxDDEServer
*DDEFindServer(const wxString
& s
)
259 wxNode
*node
= wxDDEServerObjects
.First();
260 wxDDEServer
*found
= NULL
;
261 while (node
&& !found
)
263 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
265 if (object
->GetServiceName() == s
)
267 else node
= node
->Next();
272 // ----------------------------------------------------------------------------
274 // ----------------------------------------------------------------------------
276 wxDDEServer::wxDDEServer()
280 wxDDEServerObjects
.Append(this);
283 bool wxDDEServer::Create(const wxString
& server
)
285 m_serviceName
= server
;
287 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(server
), (HSZ
)NULL
, DNS_REGISTER
) )
289 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
298 wxDDEServer::~wxDDEServer()
300 if ( !!m_serviceName
)
302 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(m_serviceName
),
303 (HSZ
)NULL
, DNS_UNREGISTER
) )
305 DDELogError(wxString::Format(_("Failed to unregister DDE server '%s'"),
306 m_serviceName
.c_str()));
310 wxDDEServerObjects
.DeleteObject(this);
312 wxNode
*node
= m_connections
.First();
315 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
316 wxNode
*next
= node
->Next();
317 connection
->OnDisconnect(); // May delete the node implicitly
321 // If any left after this, delete them
322 node
= m_connections
.First();
325 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
326 wxNode
*next
= node
->Next();
332 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
334 return new wxDDEConnection
;
337 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
339 wxNode
*node
= m_connections
.First();
340 wxDDEConnection
*found
= NULL
;
341 while (node
&& !found
)
343 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
344 if (connection
->m_hConv
== conv
)
346 else node
= node
->Next();
351 // Only delete the entry in the map, not the actual connection
352 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
354 wxNode
*node
= m_connections
.First();
356 while (node
&& !found
)
358 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
359 if (connection
->m_hConv
== conv
)
364 else node
= node
->Next();
369 // ----------------------------------------------------------------------------
371 // ----------------------------------------------------------------------------
373 wxDDEClient::wxDDEClient()
377 wxDDEClientObjects
.Append(this);
380 wxDDEClient::~wxDDEClient()
382 wxDDEClientObjects
.DeleteObject(this);
383 wxNode
*node
= m_connections
.First();
386 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
387 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
388 node
= m_connections
.First();
392 bool wxDDEClient::ValidHost(const wxString
& /* host */)
397 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
),
398 const wxString
& server
,
399 const wxString
& topic
)
401 HCONV hConv
= DdeConnect(DDEIdInst
, DDEAtomFromString(server
), DDEAtomFromString(topic
),
405 DDELogError(wxString::Format(_("Failed to create connection to server '%s' on topic '%s'"),
406 server
.c_str(), topic
.c_str()));
410 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
413 connection
->m_hConv
= (WXHCONV
) hConv
;
414 connection
->m_topicName
= topic
;
415 connection
->m_client
= this;
416 m_connections
.Append(connection
);
421 return (wxConnectionBase
*) NULL
;
424 wxConnectionBase
*wxDDEClient::OnMakeConnection()
426 return new wxDDEConnection
;
429 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
431 wxNode
*node
= m_connections
.First();
432 wxDDEConnection
*found
= NULL
;
433 while (node
&& !found
)
435 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
436 if (connection
->m_hConv
== conv
)
438 else node
= node
->Next();
443 // Only delete the entry in the map, not the actual connection
444 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
446 wxNode
*node
= m_connections
.First();
448 while (node
&& !found
)
450 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
451 if (connection
->m_hConv
== conv
)
456 else node
= node
->Next();
461 // ----------------------------------------------------------------------------
463 // ----------------------------------------------------------------------------
465 wxDDEConnection::wxDDEConnection(char *buffer
, int size
)
469 if (DDEDefaultIPCBuffer
== NULL
)
470 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
471 m_bufPtr
= DDEDefaultIPCBuffer
;
472 m_bufSize
= DDEDefaultIPCBufferSize
;
484 m_sendingData
= NULL
;
487 wxDDEConnection::wxDDEConnection()
490 m_sendingData
= NULL
;
493 if (DDEDefaultIPCBuffer
== NULL
)
494 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
496 m_bufPtr
= DDEDefaultIPCBuffer
;
497 m_bufSize
= DDEDefaultIPCBufferSize
;
500 wxDDEConnection::~wxDDEConnection()
503 m_server
->GetConnections().DeleteObject(this);
505 m_client
->GetConnections().DeleteObject(this);
508 // Calls that CLIENT can make
509 bool wxDDEConnection::Disconnect()
511 DDEDeleteConnection(GetHConv());
513 bool ok
= DdeDisconnect(GetHConv()) != 0;
516 DDELogError(_T("Failed to disconnect from DDE server gracefully"));
522 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
527 size
= wxStrlen(data
) + 1;
530 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
540 DDELogError(_T("DDE execute request failed"));
546 char *wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
549 HSZ atom
= DDEGetAtom(item
);
551 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
557 if ( !returned_data
)
559 DDELogError(_T("DDE data request failed"));
564 DWORD len
= DdeGetData(returned_data
, (LPBYTE
)m_bufPtr
, m_bufSize
, 0);
566 DdeFreeDataHandle(returned_data
);
574 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
579 size
= wxStrlen(data
) + 1;
582 HSZ item_atom
= DDEGetAtom(item
);
583 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
591 DDELogError(_("DDE poke request failed"));
597 bool wxDDEConnection::StartAdvise(const wxString
& item
)
600 HSZ atom
= DDEGetAtom(item
);
602 bool ok
= DdeClientTransaction(NULL
, 0,
610 DDELogError(_("Failed to establish an advise loop with DDE server"));
616 bool wxDDEConnection::StopAdvise(const wxString
& item
)
619 HSZ atom
= DDEGetAtom(item
);
621 bool ok
= DdeClientTransaction(NULL
, 0,
629 DDELogError(_("Failed to terminate the advise loop with DDE server"));
635 // Calls that SERVER can make
636 bool wxDDEConnection::Advise(const wxString
& item
,
643 size
= wxStrlen(data
) + 1;
646 HSZ item_atom
= DDEGetAtom(item
);
647 HSZ topic_atom
= DDEGetAtom(m_topicName
);
648 m_sendingData
= data
;
652 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
655 DDELogError(_("Failed to send DDE advise notification"));
661 bool wxDDEConnection::OnDisconnect()
667 // ----------------------------------------------------------------------------
669 // ----------------------------------------------------------------------------
671 #define DDERETURN HDDEDATA
673 HDDEDATA EXPENTRY _EXPORT
674 _DDECallback(WORD wType
,
680 DWORD
WXUNUSED(lData1
),
681 DWORD
WXUNUSED(lData2
))
687 wxString topic
= DDEStringFromAtom(hsz1
),
688 srv
= DDEStringFromAtom(hsz2
);
689 wxDDEServer
*server
= DDEFindServer(srv
);
692 wxDDEConnection
*connection
=
693 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
696 connection
->m_server
= server
;
697 server
->GetConnections().Append(connection
);
698 connection
->m_hConv
= 0;
699 connection
->m_topicName
= topic
;
700 DDECurrentlyConnecting
= connection
;
701 return (DDERETURN
)(DWORD
)TRUE
;
707 case XTYP_CONNECT_CONFIRM
:
709 if (DDECurrentlyConnecting
)
711 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
712 DDECurrentlyConnecting
= NULL
;
713 return (DDERETURN
)(DWORD
)TRUE
;
718 case XTYP_DISCONNECT
:
720 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
721 if (connection
&& connection
->OnDisconnect())
723 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
724 return (DDERETURN
)(DWORD
)TRUE
;
731 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
735 DWORD len
= DdeGetData(hData
,
736 (LPBYTE
)connection
->m_bufPtr
,
737 connection
->m_bufSize
,
739 DdeFreeDataHandle(hData
);
740 if ( connection
->OnExecute(connection
->m_topicName
,
741 connection
->m_bufPtr
,
743 (wxIPCFormat
) wFmt
) )
745 return (DDERETURN
)(DWORD
)DDE_FACK
;
749 return (DDERETURN
)DDE_FNOTPROCESSED
;
754 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
758 wxString item_name
= DDEStringFromAtom(hsz2
);
761 char *data
= connection
->OnRequest(connection
->m_topicName
,
768 user_size
= wxStrlen((wxChar
*)data
) + 1;
770 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
777 return (DDERETURN
)handle
;
785 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
789 wxString item_name
= DDEStringFromAtom(hsz2
);
791 DWORD len
= DdeGetData(hData
,
792 (LPBYTE
)connection
->m_bufPtr
,
793 connection
->m_bufSize
,
795 DdeFreeDataHandle(hData
);
797 connection
->OnPoke(connection
->m_topicName
,
799 (wxChar
*)connection
->m_bufPtr
,
803 return (DDERETURN
)DDE_FACK
;
807 return (DDERETURN
)DDE_FNOTPROCESSED
;
813 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
817 wxString item_name
= DDEStringFromAtom(hsz2
);
819 return (DDERETURN
)connection
->
820 OnStartAdvise(connection
->m_topicName
, item_name
);
828 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
832 wxString item_name
= DDEStringFromAtom(hsz2
);
834 return (DDERETURN
)connection
->
835 OnStopAdvise(connection
->m_topicName
, item_name
);
843 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
845 if (connection
&& connection
->m_sendingData
)
847 HDDEDATA data
= DdeCreateDataHandle
850 (LPBYTE
)connection
->m_sendingData
,
851 connection
->m_dataSize
,
854 connection
->m_dataType
,
858 connection
->m_sendingData
= NULL
;
860 return (DDERETURN
)data
;
868 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
872 wxString item_name
= DDEStringFromAtom(hsz2
);
874 DWORD len
= DdeGetData(hData
,
875 (LPBYTE
)connection
->m_bufPtr
,
876 connection
->m_bufSize
,
878 DdeFreeDataHandle(hData
);
879 if ( connection
->OnAdvise(connection
->m_topicName
,
881 connection
->m_bufPtr
,
883 (wxIPCFormat
) wFmt
) )
885 return (DDERETURN
)(DWORD
)DDE_FACK
;
889 return (DDERETURN
)DDE_FNOTPROCESSED
;
896 // ----------------------------------------------------------------------------
897 // DDE strings and atoms
898 // ----------------------------------------------------------------------------
901 static HSZ
DDEAddAtom(const wxString
& string
)
903 HSZ atom
= DDEAtomFromString(string
);
904 wxAtomTable
.Append(string
, (wxObject
*)atom
);
908 static HSZ
DDEGetAtom(const wxString
& string
)
910 wxNode
*node
= wxAtomTable
.Find(string
);
912 return (HSZ
)node
->Data();
916 return (HSZ
)(wxAtomTable
.Find(string
)->Data());
921 static HSZ
DDEAtomFromString(const wxString
& s
)
923 wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") );
925 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, (wxChar
*) s
.c_str(), DDE_CP
);
928 DDELogError(_("Failed to create DDE string"));
934 static wxString
DDEStringFromAtom(HSZ hsz
)
936 // all DDE strings are normally limited to 255 bytes
937 static const size_t len
= 256;
940 (void)DdeQueryString(DDEIdInst
, hsz
, s
.GetWriteBuf(len
), len
, DDE_CP
);
946 // ----------------------------------------------------------------------------
948 // ----------------------------------------------------------------------------
950 static void DDELogError(const wxString
& s
, UINT error
)
954 error
= DdeGetLastError(DDEIdInst
);
957 wxLogError(s
+ _T(": ") + DDEGetErrorMsg(error
));
960 static wxString
DDEGetErrorMsg(UINT error
)
965 case DMLERR_NO_ERROR
:
966 err
= _("no DDE error.");
969 case DMLERR_ADVACKTIMEOUT
:
970 err
= _("a request for a synchronous advise transaction has timed out.");
973 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
975 case DMLERR_DATAACKTIMEOUT
:
976 err
= _("a request for a synchronous data transaction has timed out.");
978 case DMLERR_DLL_NOT_INITIALIZED
:
979 err
= _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
981 case DMLERR_DLL_USAGE
:
982 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.");
984 case DMLERR_EXECACKTIMEOUT
:
985 err
= _("a request for a synchronous execute transaction has timed out.");
987 case DMLERR_INVALIDPARAMETER
:
988 err
= _("a parameter failed to be validated by the DDEML.");
990 case DMLERR_LOW_MEMORY
:
991 err
= _("a DDEML application has created a prolonged race condition.");
993 case DMLERR_MEMORY_ERROR
:
994 err
= _("a memory allocation failed.");
996 case DMLERR_NO_CONV_ESTABLISHED
:
997 err
= _("a client's attempt to establish a conversation has failed.");
999 case DMLERR_NOTPROCESSED
:
1000 err
= _("a transaction failed.");
1002 case DMLERR_POKEACKTIMEOUT
:
1003 err
= _("a request for a synchronous poke transaction has timed out.");
1005 case DMLERR_POSTMSG_FAILED
:
1006 err
= _("an internal call to the PostMessage function has failed. ");
1008 case DMLERR_REENTRANCY
:
1009 err
= _("reentrancy problem.");
1011 case DMLERR_SERVER_DIED
:
1012 err
= _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1014 case DMLERR_SYS_ERROR
:
1015 err
= _("an internal error has occurred in the DDEML.");
1017 case DMLERR_UNADVACKTIMEOUT
:
1018 err
= _("a request to end an advise transaction has timed out.");
1020 case DMLERR_UNFOUND_QUEUE_ID
:
1021 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.");
1024 err
.Printf(_("Unknown DDE error %08x"), error
);