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"
41 #include "wx/msw/private.h"
47 #if defined(__TWIN32__) || defined(__GNUWIN32_OLD__)
48 #include "wx/msw/gnuwin32/extra.h"
51 // ----------------------------------------------------------------------------
52 // macros and constants
53 // ----------------------------------------------------------------------------
58 #define _EXPORT _export
62 #define DDE_CP CP_WINUNICODE
64 #define DDE_CP CP_WINANSI
67 #define GetHConv() ((HCONV)m_hConv)
69 // default timeout for DDE operations (5sec)
70 #define DDE_TIMEOUT 5000
72 // ----------------------------------------------------------------------------
74 // ----------------------------------------------------------------------------
76 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
77 static void DDEDeleteConnection(HCONV hConv
);
78 static wxDDEServer
*DDEFindServer(const wxString
& s
);
80 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(WORD wType
,
89 // Add topic name to atom table before using in conversations
90 static HSZ
DDEAddAtom(const wxString
& string
);
91 static HSZ
DDEGetAtom(const wxString
& string
);
94 static HSZ
DDEAtomFromString(const wxString
& s
);
95 static wxString
DDEStringFromAtom(HSZ hsz
);
98 static wxString
DDEGetErrorMsg(UINT error
);
99 static void DDELogError(const wxString
& s
, UINT error
= DMLERR_NO_ERROR
);
101 // ----------------------------------------------------------------------------
103 // ----------------------------------------------------------------------------
105 static DWORD DDEIdInst
= 0L;
106 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
108 static wxList
wxAtomTable(wxKEY_STRING
);
109 static wxList wxDDEClientObjects
;
110 static wxList wxDDEServerObjects
;
112 char *DDEDefaultIPCBuffer
= NULL
;
113 int DDEDefaultIPCBufferSize
= 0;
114 static bool DDEInitialized
= FALSE
;
116 // ----------------------------------------------------------------------------
118 // ----------------------------------------------------------------------------
120 // A module to allow DDE cleanup without calling these functions
121 // from app.cpp or from the user's application.
123 class wxDDEModule
: public wxModule
127 bool OnInit() { return TRUE
; }
128 void OnExit() { wxDDECleanUp(); }
131 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
134 // ----------------------------------------------------------------------------
136 // ----------------------------------------------------------------------------
138 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
139 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
140 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
141 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
143 // ============================================================================
145 // ============================================================================
147 // ----------------------------------------------------------------------------
148 // initialization and cleanup
149 // ----------------------------------------------------------------------------
151 extern void wxDDEInitialize()
153 if ( !DDEInitialized
)
155 // Should insert filter flags
156 PFNCALLBACK callback
= (PFNCALLBACK
)
157 MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance());
158 UINT rc
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L);
159 if ( rc
!= DMLERR_NO_ERROR
)
161 DDELogError(_T("Failed to initialize DDE"), rc
);
165 DDEInitialized
= TRUE
;
172 if ( DDEIdInst
!= 0 )
174 DdeUninitialize(DDEIdInst
);
178 delete [] DDEDefaultIPCBuffer
;
181 // ----------------------------------------------------------------------------
182 // functions working with the global connection list(s)
183 // ----------------------------------------------------------------------------
185 // Global find connection
186 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
188 wxNode
*node
= wxDDEServerObjects
.First();
189 wxDDEConnection
*found
= NULL
;
190 while (node
&& !found
)
192 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
193 found
= object
->FindConnection((WXHCONV
) hConv
);
199 node
= wxDDEClientObjects
.First();
200 while (node
&& !found
)
202 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
203 found
= object
->FindConnection((WXHCONV
) hConv
);
209 // Global delete connection
210 static void DDEDeleteConnection(HCONV hConv
)
212 wxNode
*node
= wxDDEServerObjects
.First();
214 while (node
&& !found
)
216 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
217 found
= object
->DeleteConnection((WXHCONV
) hConv
);
223 node
= wxDDEClientObjects
.First();
224 while (node
&& !found
)
226 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
227 found
= object
->DeleteConnection((WXHCONV
) hConv
);
232 // Find a server from a service name
233 static wxDDEServer
*DDEFindServer(const wxString
& s
)
235 wxNode
*node
= wxDDEServerObjects
.First();
236 wxDDEServer
*found
= NULL
;
237 while (node
&& !found
)
239 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
241 if (object
->GetServiceName() == s
)
243 else node
= node
->Next();
248 // ----------------------------------------------------------------------------
250 // ----------------------------------------------------------------------------
252 wxDDEServer::wxDDEServer()
256 wxDDEServerObjects
.Append(this);
259 bool wxDDEServer::Create(const wxString
& server
)
261 m_serviceName
= server
;
263 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(server
), (HSZ
)NULL
, DNS_REGISTER
) )
265 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
274 wxDDEServer::~wxDDEServer()
276 if ( !!m_serviceName
)
278 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(m_serviceName
),
279 (HSZ
)NULL
, DNS_UNREGISTER
) )
281 DDELogError(wxString::Format(_("Failed to unregister DDE server '%s'"),
282 m_serviceName
.c_str()));
286 wxDDEServerObjects
.DeleteObject(this);
288 wxNode
*node
= m_connections
.First();
291 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
292 wxNode
*next
= node
->Next();
293 connection
->OnDisconnect(); // May delete the node implicitly
297 // If any left after this, delete them
298 node
= m_connections
.First();
301 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
302 wxNode
*next
= node
->Next();
308 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
310 return new wxDDEConnection
;
313 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
315 wxNode
*node
= m_connections
.First();
316 wxDDEConnection
*found
= NULL
;
317 while (node
&& !found
)
319 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
320 if (connection
->m_hConv
== conv
)
322 else node
= node
->Next();
327 // Only delete the entry in the map, not the actual connection
328 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
330 wxNode
*node
= m_connections
.First();
332 while (node
&& !found
)
334 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
335 if (connection
->m_hConv
== conv
)
340 else node
= node
->Next();
345 // ----------------------------------------------------------------------------
347 // ----------------------------------------------------------------------------
349 wxDDEClient::wxDDEClient()
353 wxDDEClientObjects
.Append(this);
356 wxDDEClient::~wxDDEClient()
358 wxDDEClientObjects
.DeleteObject(this);
359 wxNode
*node
= m_connections
.First();
362 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
363 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
364 node
= m_connections
.First();
368 bool wxDDEClient::ValidHost(const wxString
& /* host */)
373 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
),
374 const wxString
& server
,
375 const wxString
& topic
)
377 HCONV hConv
= DdeConnect(DDEIdInst
, DDEAtomFromString(server
), DDEAtomFromString(topic
),
381 DDELogError(wxString::Format(_("Failed to create connection to "
382 "server '%s' on topic '%s'"),
383 server
.c_str(), topic
.c_str()));
387 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
390 connection
->m_hConv
= (WXHCONV
) hConv
;
391 connection
->m_topicName
= topic
;
392 connection
->m_client
= this;
393 m_connections
.Append(connection
);
398 return (wxConnectionBase
*) NULL
;
401 wxConnectionBase
*wxDDEClient::OnMakeConnection()
403 return new wxDDEConnection
;
406 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
408 wxNode
*node
= m_connections
.First();
409 wxDDEConnection
*found
= NULL
;
410 while (node
&& !found
)
412 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
413 if (connection
->m_hConv
== conv
)
415 else node
= node
->Next();
420 // Only delete the entry in the map, not the actual connection
421 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
423 wxNode
*node
= m_connections
.First();
425 while (node
&& !found
)
427 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
428 if (connection
->m_hConv
== conv
)
433 else node
= node
->Next();
438 // ----------------------------------------------------------------------------
440 // ----------------------------------------------------------------------------
442 wxDDEConnection::wxDDEConnection(char *buffer
, int size
)
446 if (DDEDefaultIPCBuffer
== NULL
)
447 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
448 m_bufPtr
= DDEDefaultIPCBuffer
;
449 m_bufSize
= DDEDefaultIPCBufferSize
;
461 m_sendingData
= NULL
;
464 wxDDEConnection::wxDDEConnection()
467 m_sendingData
= NULL
;
470 if (DDEDefaultIPCBuffer
== NULL
)
471 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
473 m_bufPtr
= DDEDefaultIPCBuffer
;
474 m_bufSize
= DDEDefaultIPCBufferSize
;
477 wxDDEConnection::~wxDDEConnection()
480 m_server
->GetConnections().DeleteObject(this);
482 m_client
->GetConnections().DeleteObject(this);
485 // Calls that CLIENT can make
486 bool wxDDEConnection::Disconnect()
488 DDEDeleteConnection(GetHConv());
490 bool ok
= DdeDisconnect(GetHConv()) != 0;
493 DDELogError(_T("Failed to disconnect from DDE server gracefully"));
499 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
504 size
= wxStrlen(data
) + 1;
507 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
516 DDELogError(_T("DDE execute request failed"));
522 char *wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
525 HSZ atom
= DDEGetAtom(item
);
527 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
533 if ( !returned_data
)
535 DDELogError(_T("DDE data request failed"));
540 DWORD len
= DdeGetData(returned_data
, (LPBYTE
)m_bufPtr
, m_bufSize
, 0);
542 DdeFreeDataHandle(returned_data
);
550 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
555 size
= wxStrlen(data
) + 1;
558 HSZ item_atom
= DDEGetAtom(item
);
559 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
567 DDELogError(_("DDE poke request failed"));
573 bool wxDDEConnection::StartAdvise(const wxString
& item
)
576 HSZ atom
= DDEGetAtom(item
);
578 bool ok
= DdeClientTransaction(NULL
, 0,
586 DDELogError(_("Failed to establish an advise loop with DDE server"));
592 bool wxDDEConnection::StopAdvise(const wxString
& item
)
595 HSZ atom
= DDEGetAtom(item
);
597 bool ok
= DdeClientTransaction(NULL
, 0,
605 DDELogError(_("Failed to terminate the advise loop with DDE server"));
611 // Calls that SERVER can make
612 bool wxDDEConnection::Advise(const wxString
& item
,
619 size
= wxStrlen(data
) + 1;
622 HSZ item_atom
= DDEGetAtom(item
);
623 HSZ topic_atom
= DDEGetAtom(m_topicName
);
624 m_sendingData
= data
;
628 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
631 DDELogError(_("Failed to send DDE advise notification"));
637 bool wxDDEConnection::OnDisconnect()
643 // ----------------------------------------------------------------------------
645 // ----------------------------------------------------------------------------
647 #define DDERETURN HDDEDATA
649 HDDEDATA EXPENTRY _EXPORT
650 _DDECallback(WORD wType
,
656 DWORD
WXUNUSED(lData1
),
657 DWORD
WXUNUSED(lData2
))
663 wxString topic
= DDEStringFromAtom(hsz1
),
664 srv
= DDEStringFromAtom(hsz2
);
665 wxDDEServer
*server
= DDEFindServer(srv
);
668 wxDDEConnection
*connection
=
669 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
672 connection
->m_server
= server
;
673 server
->GetConnections().Append(connection
);
674 connection
->m_hConv
= 0;
675 connection
->m_topicName
= topic
;
676 DDECurrentlyConnecting
= connection
;
677 return (DDERETURN
)(DWORD
)TRUE
;
683 case XTYP_CONNECT_CONFIRM
:
685 if (DDECurrentlyConnecting
)
687 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
688 DDECurrentlyConnecting
= NULL
;
689 return (DDERETURN
)(DWORD
)TRUE
;
694 case XTYP_DISCONNECT
:
696 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
697 if (connection
&& connection
->OnDisconnect())
699 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
700 return (DDERETURN
)(DWORD
)TRUE
;
707 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
711 DWORD len
= DdeGetData(hData
,
712 (LPBYTE
)connection
->m_bufPtr
,
713 connection
->m_bufSize
,
715 DdeFreeDataHandle(hData
);
716 if ( connection
->OnExecute(connection
->m_topicName
,
717 connection
->m_bufPtr
,
719 (wxIPCFormat
) wFmt
) )
721 return (DDERETURN
)(DWORD
)DDE_FACK
;
725 return (DDERETURN
)DDE_FNOTPROCESSED
;
730 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
734 wxString item_name
= DDEStringFromAtom(hsz2
);
737 char *data
= connection
->OnRequest(connection
->m_topicName
,
744 user_size
= wxStrlen(data
) + 1;
746 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
753 return (DDERETURN
)handle
;
761 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
765 wxString item_name
= DDEStringFromAtom(hsz2
);
767 DWORD len
= DdeGetData(hData
,
768 (LPBYTE
)connection
->m_bufPtr
,
769 connection
->m_bufSize
,
771 DdeFreeDataHandle(hData
);
773 connection
->OnPoke(connection
->m_topicName
,
775 connection
->m_bufPtr
,
779 return (DDERETURN
)DDE_FACK
;
783 return (DDERETURN
)DDE_FNOTPROCESSED
;
789 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
793 wxString item_name
= DDEStringFromAtom(hsz2
);
795 return (DDERETURN
)connection
->
796 OnStartAdvise(connection
->m_topicName
, item_name
);
804 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
808 wxString item_name
= DDEStringFromAtom(hsz2
);
810 return (DDERETURN
)connection
->
811 OnStopAdvise(connection
->m_topicName
, item_name
);
819 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
821 if (connection
&& connection
->m_sendingData
)
823 HDDEDATA data
= DdeCreateDataHandle
826 (LPBYTE
)connection
->m_sendingData
,
827 connection
->m_dataSize
,
830 connection
->m_dataType
,
834 connection
->m_sendingData
= NULL
;
836 return (DDERETURN
)data
;
844 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
848 wxString item_name
= DDEStringFromAtom(hsz2
);
850 DWORD len
= DdeGetData(hData
,
851 (LPBYTE
)connection
->m_bufPtr
,
852 connection
->m_bufSize
,
854 DdeFreeDataHandle(hData
);
855 if ( connection
->OnAdvise(connection
->m_topicName
,
857 connection
->m_bufPtr
,
859 (wxIPCFormat
) wFmt
) )
861 return (DDERETURN
)(DWORD
)DDE_FACK
;
865 return (DDERETURN
)DDE_FNOTPROCESSED
;
872 // ----------------------------------------------------------------------------
873 // DDE strings and atoms
874 // ----------------------------------------------------------------------------
877 static HSZ
DDEAddAtom(const wxString
& string
)
879 HSZ atom
= DDEAtomFromString(string
);
880 wxAtomTable
.Append(string
, (wxObject
*)atom
);
884 static HSZ
DDEGetAtom(const wxString
& string
)
886 wxNode
*node
= wxAtomTable
.Find(string
);
888 return (HSZ
)node
->Data();
892 return (HSZ
)(wxAtomTable
.Find(string
)->Data());
897 static HSZ
DDEAtomFromString(const wxString
& s
)
899 wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") );
901 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, s
, DDE_CP
);
904 DDELogError(_("Failed to create DDE string"));
910 static wxString
DDEStringFromAtom(HSZ hsz
)
912 // all DDE strings are normally limited to 255 bytes
913 static const size_t len
= 256;
916 (void)DdeQueryString(DDEIdInst
, hsz
, s
.GetWriteBuf(len
), len
, DDE_CP
);
922 // ----------------------------------------------------------------------------
924 // ----------------------------------------------------------------------------
926 static void DDELogError(const wxString
& s
, UINT error
)
930 error
= DdeGetLastError(DDEIdInst
);
933 wxLogError(s
+ _T(": ") + DDEGetErrorMsg(error
));
936 static wxString
DDEGetErrorMsg(UINT error
)
941 case DMLERR_NO_ERROR
:
942 err
= _("no DDE error.");
945 case DMLERR_ADVACKTIMEOUT
:
946 err
= _("a request for a synchronous advise transaction has timed out.");
949 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
951 case DMLERR_DATAACKTIMEOUT
:
952 err
= _("a request for a synchronous data transaction has timed out.");
954 case DMLERR_DLL_NOT_INITIALIZED
:
955 err
= _("a DDEML function was called without first calling the DdeInitialize function,\n\ror an invalid instance identifier\n\rwas passed to a DDEML function.");
957 case DMLERR_DLL_USAGE
:
958 err
= _("an application initialized as APPCLASS_MONITOR has\n\rattempted to perform a DDE transaction,\n\ror an application initialized as APPCMD_CLIENTONLY has \n\rattempted to perform server transactions.");
960 case DMLERR_EXECACKTIMEOUT
:
961 err
= _("a request for a synchronous execute transaction has timed out.");
963 case DMLERR_INVALIDPARAMETER
:
964 err
= _("a parameter failed to be validated by the DDEML.");
966 case DMLERR_LOW_MEMORY
:
967 err
= _("a DDEML application has created a prolonged race condition.");
969 case DMLERR_MEMORY_ERROR
:
970 err
= _("a memory allocation failed.");
972 case DMLERR_NO_CONV_ESTABLISHED
:
973 err
= _("a client's attempt to establish a conversation has failed.");
975 case DMLERR_NOTPROCESSED
:
976 err
= _("a transaction failed.");
978 case DMLERR_POKEACKTIMEOUT
:
979 err
= _("a request for a synchronous poke transaction has timed out.");
981 case DMLERR_POSTMSG_FAILED
:
982 err
= _("an internal call to the PostMessage function has failed. ");
984 case DMLERR_REENTRANCY
:
985 err
= _("reentrancy problem.");
987 case DMLERR_SERVER_DIED
:
988 err
= _("a server-side transaction was attempted on a conversation\n\rthat was terminated by the client, or the server\n\rterminated before completing a transaction.");
990 case DMLERR_SYS_ERROR
:
991 err
= _("an internal error has occurred in the DDEML.");
993 case DMLERR_UNADVACKTIMEOUT
:
994 err
= _("a request to end an advise transaction has timed out.");
996 case DMLERR_UNFOUND_QUEUE_ID
:
997 err
= _("an invalid transaction identifier was passed to a DDEML function.\n\rOnce the application has returned from an XTYP_XACT_COMPLETE callback,\n\rthe transaction identifier for that callback is no longer valid.");
1000 err
.Printf(_("Unknown DDE error %08x"), error
);