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"
49 #if defined(__TWIN32__) || defined(__GNUWIN32_OLD__)
50 #include "wx/msw/gnuwin32/extra.h"
53 // ----------------------------------------------------------------------------
54 // macros and constants
55 // ----------------------------------------------------------------------------
60 #define _EXPORT _export
64 #define DDE_CP CP_WINUNICODE
66 #define DDE_CP CP_WINANSI
69 #define GetHConv() ((HCONV)m_hConv)
71 // default timeout for DDE operations (5sec)
72 #define DDE_TIMEOUT 5000
74 // ----------------------------------------------------------------------------
76 // ----------------------------------------------------------------------------
78 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
79 static void DDEDeleteConnection(HCONV hConv
);
80 static wxDDEServer
*DDEFindServer(const wxString
& s
);
82 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(WORD wType
,
91 // Add topic name to atom table before using in conversations
92 static HSZ
DDEAddAtom(const wxString
& string
);
93 static HSZ
DDEGetAtom(const wxString
& string
);
96 static HSZ
DDEAtomFromString(const wxString
& s
);
97 static wxString
DDEStringFromAtom(HSZ hsz
);
100 static wxString
DDEGetErrorMsg(UINT error
);
101 static void DDELogError(const wxString
& s
, UINT error
= DMLERR_NO_ERROR
);
103 // ----------------------------------------------------------------------------
105 // ----------------------------------------------------------------------------
107 static DWORD DDEIdInst
= 0L;
108 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
110 static wxList
wxAtomTable(wxKEY_STRING
);
111 static wxList wxDDEClientObjects
;
112 static wxList wxDDEServerObjects
;
114 char *DDEDefaultIPCBuffer
= NULL
;
115 int DDEDefaultIPCBufferSize
= 0;
116 static bool DDEInitialized
= FALSE
;
118 // ----------------------------------------------------------------------------
120 // ----------------------------------------------------------------------------
122 // A module to allow DDE cleanup without calling these functions
123 // from app.cpp or from the user's application.
125 class wxDDEModule
: public wxModule
129 bool OnInit() { return TRUE
; }
130 void OnExit() { wxDDECleanUp(); }
133 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
136 // ----------------------------------------------------------------------------
138 // ----------------------------------------------------------------------------
140 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
141 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
142 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
143 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
145 // ============================================================================
147 // ============================================================================
149 // ----------------------------------------------------------------------------
150 // initialization and cleanup
151 // ----------------------------------------------------------------------------
153 extern void wxDDEInitialize()
155 if ( !DDEInitialized
)
157 // Should insert filter flags
158 PFNCALLBACK callback
= (PFNCALLBACK
)
159 MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance());
160 UINT rc
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L);
161 if ( rc
!= DMLERR_NO_ERROR
)
163 DDELogError(_T("Failed to initialize DDE"), rc
);
167 DDEInitialized
= TRUE
;
174 if ( DDEIdInst
!= 0 )
176 DdeUninitialize(DDEIdInst
);
180 delete [] DDEDefaultIPCBuffer
;
183 // ----------------------------------------------------------------------------
184 // functions working with the global connection list(s)
185 // ----------------------------------------------------------------------------
187 // Global find connection
188 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
190 wxNode
*node
= wxDDEServerObjects
.First();
191 wxDDEConnection
*found
= NULL
;
192 while (node
&& !found
)
194 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
195 found
= object
->FindConnection((WXHCONV
) hConv
);
201 node
= wxDDEClientObjects
.First();
202 while (node
&& !found
)
204 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
205 found
= object
->FindConnection((WXHCONV
) hConv
);
211 // Global delete connection
212 static void DDEDeleteConnection(HCONV hConv
)
214 wxNode
*node
= wxDDEServerObjects
.First();
216 while (node
&& !found
)
218 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
219 found
= object
->DeleteConnection((WXHCONV
) hConv
);
225 node
= wxDDEClientObjects
.First();
226 while (node
&& !found
)
228 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
229 found
= object
->DeleteConnection((WXHCONV
) hConv
);
234 // Find a server from a service name
235 static wxDDEServer
*DDEFindServer(const wxString
& s
)
237 wxNode
*node
= wxDDEServerObjects
.First();
238 wxDDEServer
*found
= NULL
;
239 while (node
&& !found
)
241 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
243 if (object
->GetServiceName() == s
)
245 else node
= node
->Next();
250 // ----------------------------------------------------------------------------
252 // ----------------------------------------------------------------------------
254 wxDDEServer::wxDDEServer()
258 wxDDEServerObjects
.Append(this);
261 bool wxDDEServer::Create(const wxString
& server
)
263 m_serviceName
= server
;
265 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(server
), (HSZ
)NULL
, DNS_REGISTER
) )
267 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
276 wxDDEServer::~wxDDEServer()
278 if ( !!m_serviceName
)
280 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(m_serviceName
),
281 (HSZ
)NULL
, DNS_UNREGISTER
) )
283 DDELogError(wxString::Format(_("Failed to unregister DDE server '%s'"),
284 m_serviceName
.c_str()));
288 wxDDEServerObjects
.DeleteObject(this);
290 wxNode
*node
= m_connections
.First();
293 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
294 wxNode
*next
= node
->Next();
295 connection
->OnDisconnect(); // May delete the node implicitly
299 // If any left after this, delete them
300 node
= m_connections
.First();
303 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
304 wxNode
*next
= node
->Next();
310 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
312 return new wxDDEConnection
;
315 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
317 wxNode
*node
= m_connections
.First();
318 wxDDEConnection
*found
= NULL
;
319 while (node
&& !found
)
321 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
322 if (connection
->m_hConv
== conv
)
324 else node
= node
->Next();
329 // Only delete the entry in the map, not the actual connection
330 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
332 wxNode
*node
= m_connections
.First();
334 while (node
&& !found
)
336 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
337 if (connection
->m_hConv
== conv
)
342 else node
= node
->Next();
347 // ----------------------------------------------------------------------------
349 // ----------------------------------------------------------------------------
351 wxDDEClient::wxDDEClient()
355 wxDDEClientObjects
.Append(this);
358 wxDDEClient::~wxDDEClient()
360 wxDDEClientObjects
.DeleteObject(this);
361 wxNode
*node
= m_connections
.First();
364 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
365 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
366 node
= m_connections
.First();
370 bool wxDDEClient::ValidHost(const wxString
& /* host */)
375 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
),
376 const wxString
& server
,
377 const wxString
& topic
)
379 HCONV hConv
= DdeConnect(DDEIdInst
, DDEAtomFromString(server
), DDEAtomFromString(topic
),
383 DDELogError(wxString::Format(_("Failed to create connection to "
384 "server '%s' on topic '%s'"),
385 server
.c_str(), topic
.c_str()));
389 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
392 connection
->m_hConv
= (WXHCONV
) hConv
;
393 connection
->m_topicName
= topic
;
394 connection
->m_client
= this;
395 m_connections
.Append(connection
);
400 return (wxConnectionBase
*) NULL
;
403 wxConnectionBase
*wxDDEClient::OnMakeConnection()
405 return new wxDDEConnection
;
408 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
410 wxNode
*node
= m_connections
.First();
411 wxDDEConnection
*found
= NULL
;
412 while (node
&& !found
)
414 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
415 if (connection
->m_hConv
== conv
)
417 else node
= node
->Next();
422 // Only delete the entry in the map, not the actual connection
423 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
425 wxNode
*node
= m_connections
.First();
427 while (node
&& !found
)
429 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
430 if (connection
->m_hConv
== conv
)
435 else node
= node
->Next();
440 // ----------------------------------------------------------------------------
442 // ----------------------------------------------------------------------------
444 wxDDEConnection::wxDDEConnection(char *buffer
, int size
)
448 if (DDEDefaultIPCBuffer
== NULL
)
449 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
450 m_bufPtr
= DDEDefaultIPCBuffer
;
451 m_bufSize
= DDEDefaultIPCBufferSize
;
463 m_sendingData
= NULL
;
466 wxDDEConnection::wxDDEConnection()
469 m_sendingData
= NULL
;
472 if (DDEDefaultIPCBuffer
== NULL
)
473 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
475 m_bufPtr
= DDEDefaultIPCBuffer
;
476 m_bufSize
= DDEDefaultIPCBufferSize
;
479 wxDDEConnection::~wxDDEConnection()
482 m_server
->GetConnections().DeleteObject(this);
484 m_client
->GetConnections().DeleteObject(this);
487 // Calls that CLIENT can make
488 bool wxDDEConnection::Disconnect()
490 DDEDeleteConnection(GetHConv());
492 bool ok
= DdeDisconnect(GetHConv()) != 0;
495 DDELogError(_T("Failed to disconnect from DDE server gracefully"));
501 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
506 size
= wxStrlen(data
) + 1;
509 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
518 DDELogError(_T("DDE execute request failed"));
524 char *wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
527 HSZ atom
= DDEGetAtom(item
);
529 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
535 if ( !returned_data
)
537 DDELogError(_T("DDE data request failed"));
542 DWORD len
= DdeGetData(returned_data
, (LPBYTE
)m_bufPtr
, m_bufSize
, 0);
544 DdeFreeDataHandle(returned_data
);
552 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
557 size
= wxStrlen(data
) + 1;
560 HSZ item_atom
= DDEGetAtom(item
);
561 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
569 DDELogError(_("DDE poke request failed"));
575 bool wxDDEConnection::StartAdvise(const wxString
& item
)
578 HSZ atom
= DDEGetAtom(item
);
580 bool ok
= DdeClientTransaction(NULL
, 0,
588 DDELogError(_("Failed to establish an advise loop with DDE server"));
594 bool wxDDEConnection::StopAdvise(const wxString
& item
)
597 HSZ atom
= DDEGetAtom(item
);
599 bool ok
= DdeClientTransaction(NULL
, 0,
607 DDELogError(_("Failed to terminate the advise loop with DDE server"));
613 // Calls that SERVER can make
614 bool wxDDEConnection::Advise(const wxString
& item
,
621 size
= wxStrlen(data
) + 1;
624 HSZ item_atom
= DDEGetAtom(item
);
625 HSZ topic_atom
= DDEGetAtom(m_topicName
);
626 m_sendingData
= data
;
630 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
633 DDELogError(_("Failed to send DDE advise notification"));
639 bool wxDDEConnection::OnDisconnect()
645 // ----------------------------------------------------------------------------
647 // ----------------------------------------------------------------------------
649 #define DDERETURN HDDEDATA
651 HDDEDATA EXPENTRY _EXPORT
652 _DDECallback(WORD wType
,
658 DWORD
WXUNUSED(lData1
),
659 DWORD
WXUNUSED(lData2
))
665 wxString topic
= DDEStringFromAtom(hsz1
),
666 srv
= DDEStringFromAtom(hsz2
);
667 wxDDEServer
*server
= DDEFindServer(srv
);
670 wxDDEConnection
*connection
=
671 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
674 connection
->m_server
= server
;
675 server
->GetConnections().Append(connection
);
676 connection
->m_hConv
= 0;
677 connection
->m_topicName
= topic
;
678 DDECurrentlyConnecting
= connection
;
679 return (DDERETURN
)(DWORD
)TRUE
;
685 case XTYP_CONNECT_CONFIRM
:
687 if (DDECurrentlyConnecting
)
689 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
690 DDECurrentlyConnecting
= NULL
;
691 return (DDERETURN
)(DWORD
)TRUE
;
696 case XTYP_DISCONNECT
:
698 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
699 if (connection
&& connection
->OnDisconnect())
701 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
702 return (DDERETURN
)(DWORD
)TRUE
;
709 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
713 DWORD len
= DdeGetData(hData
,
714 (LPBYTE
)connection
->m_bufPtr
,
715 connection
->m_bufSize
,
717 DdeFreeDataHandle(hData
);
718 if ( connection
->OnExecute(connection
->m_topicName
,
719 connection
->m_bufPtr
,
721 (wxIPCFormat
) wFmt
) )
723 return (DDERETURN
)(DWORD
)DDE_FACK
;
727 return (DDERETURN
)DDE_FNOTPROCESSED
;
732 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
736 wxString item_name
= DDEStringFromAtom(hsz2
);
739 char *data
= connection
->OnRequest(connection
->m_topicName
,
746 user_size
= wxStrlen(data
) + 1;
748 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
755 return (DDERETURN
)handle
;
763 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
767 wxString item_name
= DDEStringFromAtom(hsz2
);
769 DWORD len
= DdeGetData(hData
,
770 (LPBYTE
)connection
->m_bufPtr
,
771 connection
->m_bufSize
,
773 DdeFreeDataHandle(hData
);
775 connection
->OnPoke(connection
->m_topicName
,
777 connection
->m_bufPtr
,
781 return (DDERETURN
)DDE_FACK
;
785 return (DDERETURN
)DDE_FNOTPROCESSED
;
791 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
795 wxString item_name
= DDEStringFromAtom(hsz2
);
797 return (DDERETURN
)connection
->
798 OnStartAdvise(connection
->m_topicName
, item_name
);
806 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
810 wxString item_name
= DDEStringFromAtom(hsz2
);
812 return (DDERETURN
)connection
->
813 OnStopAdvise(connection
->m_topicName
, item_name
);
821 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
823 if (connection
&& connection
->m_sendingData
)
825 HDDEDATA data
= DdeCreateDataHandle
828 (LPBYTE
)connection
->m_sendingData
,
829 connection
->m_dataSize
,
832 connection
->m_dataType
,
836 connection
->m_sendingData
= NULL
;
838 return (DDERETURN
)data
;
846 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
850 wxString item_name
= DDEStringFromAtom(hsz2
);
852 DWORD len
= DdeGetData(hData
,
853 (LPBYTE
)connection
->m_bufPtr
,
854 connection
->m_bufSize
,
856 DdeFreeDataHandle(hData
);
857 if ( connection
->OnAdvise(connection
->m_topicName
,
859 connection
->m_bufPtr
,
861 (wxIPCFormat
) wFmt
) )
863 return (DDERETURN
)(DWORD
)DDE_FACK
;
867 return (DDERETURN
)DDE_FNOTPROCESSED
;
874 // ----------------------------------------------------------------------------
875 // DDE strings and atoms
876 // ----------------------------------------------------------------------------
879 static HSZ
DDEAddAtom(const wxString
& string
)
881 HSZ atom
= DDEAtomFromString(string
);
882 wxAtomTable
.Append(string
, (wxObject
*)atom
);
886 static HSZ
DDEGetAtom(const wxString
& string
)
888 wxNode
*node
= wxAtomTable
.Find(string
);
890 return (HSZ
)node
->Data();
894 return (HSZ
)(wxAtomTable
.Find(string
)->Data());
899 static HSZ
DDEAtomFromString(const wxString
& s
)
901 wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") );
903 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, (char*) s
.c_str(), DDE_CP
);
906 DDELogError(_("Failed to create DDE string"));
912 static wxString
DDEStringFromAtom(HSZ hsz
)
914 // all DDE strings are normally limited to 255 bytes
915 static const size_t len
= 256;
918 (void)DdeQueryString(DDEIdInst
, hsz
, s
.GetWriteBuf(len
), len
, DDE_CP
);
924 // ----------------------------------------------------------------------------
926 // ----------------------------------------------------------------------------
928 static void DDELogError(const wxString
& s
, UINT error
)
932 error
= DdeGetLastError(DDEIdInst
);
935 wxLogError(s
+ _T(": ") + DDEGetErrorMsg(error
));
938 static wxString
DDEGetErrorMsg(UINT error
)
943 case DMLERR_NO_ERROR
:
944 err
= _("no DDE error.");
947 case DMLERR_ADVACKTIMEOUT
:
948 err
= _("a request for a synchronous advise transaction has timed out.");
951 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
953 case DMLERR_DATAACKTIMEOUT
:
954 err
= _("a request for a synchronous data transaction has timed out.");
956 case DMLERR_DLL_NOT_INITIALIZED
:
957 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.");
959 case DMLERR_DLL_USAGE
:
960 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.");
962 case DMLERR_EXECACKTIMEOUT
:
963 err
= _("a request for a synchronous execute transaction has timed out.");
965 case DMLERR_INVALIDPARAMETER
:
966 err
= _("a parameter failed to be validated by the DDEML.");
968 case DMLERR_LOW_MEMORY
:
969 err
= _("a DDEML application has created a prolonged race condition.");
971 case DMLERR_MEMORY_ERROR
:
972 err
= _("a memory allocation failed.");
974 case DMLERR_NO_CONV_ESTABLISHED
:
975 err
= _("a client's attempt to establish a conversation has failed.");
977 case DMLERR_NOTPROCESSED
:
978 err
= _("a transaction failed.");
980 case DMLERR_POKEACKTIMEOUT
:
981 err
= _("a request for a synchronous poke transaction has timed out.");
983 case DMLERR_POSTMSG_FAILED
:
984 err
= _("an internal call to the PostMessage function has failed. ");
986 case DMLERR_REENTRANCY
:
987 err
= _("reentrancy problem.");
989 case DMLERR_SERVER_DIED
:
990 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.");
992 case DMLERR_SYS_ERROR
:
993 err
= _("an internal error has occurred in the DDEML.");
995 case DMLERR_UNADVACKTIMEOUT
:
996 err
= _("a request to end an advise transaction has timed out.");
998 case DMLERR_UNFOUND_QUEUE_ID
:
999 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.");
1002 err
.Printf(_("Unknown DDE error %08x"), error
);