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 // 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
;
128 static wxList
wxAtomTable(wxKEY_STRING
);
129 static wxList wxDDEClientObjects
;
130 static wxList wxDDEServerObjects
;
132 static bool DDEInitialized
= FALSE
;
134 // ----------------------------------------------------------------------------
136 // ----------------------------------------------------------------------------
138 // A module to allow DDE cleanup without calling these functions
139 // from app.cpp or from the user's application.
141 class wxDDEModule
: public wxModule
145 bool OnInit() { return TRUE
; }
146 void OnExit() { wxDDECleanUp(); }
149 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
152 // ----------------------------------------------------------------------------
154 // ----------------------------------------------------------------------------
156 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
157 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
158 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
159 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
161 // ============================================================================
163 // ============================================================================
165 // ----------------------------------------------------------------------------
166 // initialization and cleanup
167 // ----------------------------------------------------------------------------
169 extern void wxDDEInitialize()
171 if ( !DDEInitialized
)
173 // Should insert filter flags
174 PFNCALLBACK callback
= (PFNCALLBACK
)
175 MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance());
176 UINT rc
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L);
177 if ( rc
!= DMLERR_NO_ERROR
)
179 DDELogError(_T("Failed to initialize DDE"), rc
);
183 DDEInitialized
= TRUE
;
190 wxDDEClientObjects
.DeleteContents(TRUE
);
191 wxDDEClientObjects
.Clear();
192 wxDDEClientObjects
.DeleteContents(FALSE
);
194 wxDDEServerObjects
.DeleteContents(TRUE
);
195 wxDDEServerObjects
.Clear();
196 wxDDEServerObjects
.DeleteContents(FALSE
);
200 if ( DDEIdInst
!= 0 )
202 DdeUninitialize(DDEIdInst
);
207 // ----------------------------------------------------------------------------
208 // functions working with the global connection list(s)
209 // ----------------------------------------------------------------------------
211 // Global find connection
212 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
214 wxNode
*node
= wxDDEServerObjects
.First();
215 wxDDEConnection
*found
= NULL
;
216 while (node
&& !found
)
218 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
219 found
= object
->FindConnection((WXHCONV
) hConv
);
225 node
= wxDDEClientObjects
.First();
226 while (node
&& !found
)
228 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
229 found
= object
->FindConnection((WXHCONV
) hConv
);
235 // Global delete connection
236 static void DDEDeleteConnection(HCONV hConv
)
238 wxNode
*node
= wxDDEServerObjects
.First();
240 while (node
&& !found
)
242 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
243 found
= object
->DeleteConnection((WXHCONV
) hConv
);
249 node
= wxDDEClientObjects
.First();
250 while (node
&& !found
)
252 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
253 found
= object
->DeleteConnection((WXHCONV
) hConv
);
258 // Find a server from a service name
259 static wxDDEServer
*DDEFindServer(const wxString
& s
)
261 wxNode
*node
= wxDDEServerObjects
.First();
262 wxDDEServer
*found
= NULL
;
263 while (node
&& !found
)
265 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
267 if (object
->GetServiceName() == s
)
269 else node
= node
->Next();
274 // ----------------------------------------------------------------------------
276 // ----------------------------------------------------------------------------
278 wxDDEServer::wxDDEServer()
282 wxDDEServerObjects
.Append(this);
285 bool wxDDEServer::Create(const wxString
& server
)
287 m_serviceName
= server
;
289 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(server
), (HSZ
)NULL
, DNS_REGISTER
) )
291 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
300 wxDDEServer::~wxDDEServer()
302 if ( !!m_serviceName
)
304 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(m_serviceName
),
305 (HSZ
)NULL
, DNS_UNREGISTER
) )
307 DDELogError(wxString::Format(_("Failed to unregister DDE server '%s'"),
308 m_serviceName
.c_str()));
312 wxDDEServerObjects
.DeleteObject(this);
314 wxNode
*node
= m_connections
.First();
317 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
318 wxNode
*next
= node
->Next();
319 connection
->SetConnected(false);
320 connection
->OnDisconnect(); // May delete the node implicitly
324 // If any left after this, delete them
325 node
= m_connections
.First();
328 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
329 wxNode
*next
= node
->Next();
335 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
337 return new wxDDEConnection
;
340 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
342 wxNode
*node
= m_connections
.First();
343 wxDDEConnection
*found
= NULL
;
344 while (node
&& !found
)
346 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
347 if (connection
->m_hConv
== conv
)
349 else node
= node
->Next();
354 // Only delete the entry in the map, not the actual connection
355 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
357 wxNode
*node
= m_connections
.First();
359 while (node
&& !found
)
361 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
362 if (connection
->m_hConv
== conv
)
367 else node
= node
->Next();
372 // ----------------------------------------------------------------------------
374 // ----------------------------------------------------------------------------
376 wxDDEClient::wxDDEClient()
380 wxDDEClientObjects
.Append(this);
383 wxDDEClient::~wxDDEClient()
385 wxDDEClientObjects
.DeleteObject(this);
386 wxNode
*node
= m_connections
.First();
389 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
390 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
391 node
= m_connections
.First();
395 bool wxDDEClient::ValidHost(const wxString
& /* host */)
400 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
),
401 const wxString
& server
,
402 const wxString
& topic
)
404 HCONV hConv
= DdeConnect(DDEIdInst
, DDEAtomFromString(server
), DDEAtomFromString(topic
),
408 DDELogError(wxString::Format(_("Failed to create connection to server '%s' on topic '%s'"),
409 server
.c_str(), topic
.c_str()));
413 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
416 connection
->m_hConv
= (WXHCONV
) hConv
;
417 connection
->m_topicName
= topic
;
418 connection
->m_client
= this;
419 m_connections
.Append(connection
);
424 return (wxConnectionBase
*) NULL
;
427 wxConnectionBase
*wxDDEClient::OnMakeConnection()
429 return new wxDDEConnection
;
432 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
434 wxNode
*node
= m_connections
.First();
435 wxDDEConnection
*found
= NULL
;
436 while (node
&& !found
)
438 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
439 if (connection
->m_hConv
== conv
)
441 else node
= node
->Next();
446 // Only delete the entry in the map, not the actual connection
447 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
449 wxNode
*node
= m_connections
.First();
451 while (node
&& !found
)
453 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
454 if (connection
->m_hConv
== conv
)
459 else node
= node
->Next();
464 // ----------------------------------------------------------------------------
466 // ----------------------------------------------------------------------------
468 wxDDEConnection::wxDDEConnection(wxChar
*buffer
, int size
)
469 : wxConnectionBase(buffer
, size
)
475 m_sendingData
= NULL
;
478 wxDDEConnection::wxDDEConnection()
482 m_sendingData
= NULL
;
487 wxDDEConnection::~wxDDEConnection()
491 m_server
->GetConnections().DeleteObject(this);
493 m_client
->GetConnections().DeleteObject(this);
496 // Calls that CLIENT can make
497 bool wxDDEConnection::Disconnect()
499 if ( !GetConnected() )
502 DDEDeleteConnection(GetHConv());
504 bool ok
= DdeDisconnect(GetHConv()) != 0;
507 DDELogError(_T("Failed to disconnect from DDE server gracefully"));
510 SetConnected( false ); // so we don't try and disconnect again
515 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
520 size
= wxStrlen(data
) + 1;
523 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
533 DDELogError(_T("DDE execute request failed"));
539 wxChar
*wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
543 HSZ atom
= DDEGetAtom(item
);
545 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
551 if ( !returned_data
)
553 DDELogError(_T("DDE data request failed"));
558 DWORD len
= DdeGetData(returned_data
, NULL
, 0, 0);
560 wxChar
*data
= GetBufferAtLeast( len
);
561 wxASSERT_MSG(data
!= NULL
,
562 _T("Buffer too small in wxDDEConnection::Request") );
563 DdeGetData(returned_data
, (LPBYTE
)data
, len
, 0);
565 DdeFreeDataHandle(returned_data
);
573 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
578 size
= wxStrlen(data
) + 1;
581 HSZ item_atom
= DDEGetAtom(item
);
582 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
590 DDELogError(_("DDE poke request failed"));
596 bool wxDDEConnection::StartAdvise(const wxString
& item
)
599 HSZ atom
= DDEGetAtom(item
);
601 bool ok
= DdeClientTransaction(NULL
, 0,
609 DDELogError(_("Failed to establish an advise loop with DDE server"));
615 bool wxDDEConnection::StopAdvise(const wxString
& item
)
618 HSZ atom
= DDEGetAtom(item
);
620 bool ok
= DdeClientTransaction(NULL
, 0,
628 DDELogError(_("Failed to terminate the advise loop with DDE server"));
634 // Calls that SERVER can make
635 bool wxDDEConnection::Advise(const wxString
& item
,
642 size
= wxStrlen(data
) + 1;
645 HSZ item_atom
= DDEGetAtom(item
);
646 HSZ topic_atom
= DDEGetAtom(m_topicName
);
647 m_sendingData
= data
; // mrf: potential for scope problems here?
651 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
654 DDELogError(_("Failed to send DDE advise notification"));
660 bool wxDDEConnection::OnDisconnect()
666 // ----------------------------------------------------------------------------
668 // ----------------------------------------------------------------------------
670 #define DDERETURN HDDEDATA
672 HDDEDATA EXPENTRY _EXPORT
673 _DDECallback(WORD wType
,
679 DWORD
WXUNUSED(lData1
),
680 DWORD
WXUNUSED(lData2
))
686 wxString topic
= DDEStringFromAtom(hsz1
),
687 srv
= DDEStringFromAtom(hsz2
);
688 wxDDEServer
*server
= DDEFindServer(srv
);
691 wxDDEConnection
*connection
=
692 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
695 connection
->m_server
= server
;
696 server
->GetConnections().Append(connection
);
697 connection
->m_hConv
= 0;
698 connection
->m_topicName
= topic
;
699 DDECurrentlyConnecting
= connection
;
700 return (DDERETURN
)(DWORD
)TRUE
;
706 case XTYP_CONNECT_CONFIRM
:
708 if (DDECurrentlyConnecting
)
710 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
711 DDECurrentlyConnecting
= NULL
;
712 return (DDERETURN
)(DWORD
)TRUE
;
717 case XTYP_DISCONNECT
:
719 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
722 connection
->SetConnected( false );
723 if (connection
->OnDisconnect())
725 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
726 return (DDERETURN
)(DWORD
)TRUE
;
734 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
738 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
740 wxChar
*data
= connection
->GetBufferAtLeast( len
);
741 wxASSERT_MSG(data
!= NULL
,
742 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
744 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
746 DdeFreeDataHandle(hData
);
748 if ( connection
->OnExecute(connection
->m_topicName
,
751 (wxIPCFormat
) wFmt
) )
753 return (DDERETURN
)(DWORD
)DDE_FACK
;
757 return (DDERETURN
)DDE_FNOTPROCESSED
;
762 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
766 wxString item_name
= DDEStringFromAtom(hsz2
);
769 wxChar
*data
= connection
->OnRequest(connection
->m_topicName
,
776 user_size
= wxStrlen((wxChar
*)data
) + 1;
778 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
785 return (DDERETURN
)handle
;
793 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
797 wxString item_name
= DDEStringFromAtom(hsz2
);
799 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
801 wxChar
*data
= connection
->GetBufferAtLeast( len
);
802 wxASSERT_MSG(data
!= NULL
,
803 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
805 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
807 DdeFreeDataHandle(hData
);
809 connection
->OnPoke(connection
->m_topicName
,
815 return (DDERETURN
)DDE_FACK
;
819 return (DDERETURN
)DDE_FNOTPROCESSED
;
825 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
829 wxString item_name
= DDEStringFromAtom(hsz2
);
831 return (DDERETURN
)connection
->
832 OnStartAdvise(connection
->m_topicName
, item_name
);
840 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
844 wxString item_name
= DDEStringFromAtom(hsz2
);
846 return (DDERETURN
)connection
->
847 OnStopAdvise(connection
->m_topicName
, item_name
);
855 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
857 if (connection
&& connection
->m_sendingData
)
859 HDDEDATA data
= DdeCreateDataHandle
862 (LPBYTE
)connection
->m_sendingData
,
863 connection
->m_dataSize
,
866 connection
->m_dataType
,
870 connection
->m_sendingData
= NULL
;
872 return (DDERETURN
)data
;
880 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
884 wxString item_name
= DDEStringFromAtom(hsz2
);
886 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
888 wxChar
*data
= connection
->GetBufferAtLeast( len
);
889 wxASSERT_MSG(data
!= NULL
,
890 _T("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
892 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
894 DdeFreeDataHandle(hData
);
895 if ( connection
->OnAdvise(connection
->m_topicName
,
899 (wxIPCFormat
) wFmt
) )
901 return (DDERETURN
)(DWORD
)DDE_FACK
;
905 return (DDERETURN
)DDE_FNOTPROCESSED
;
912 // ----------------------------------------------------------------------------
913 // DDE strings and atoms
914 // ----------------------------------------------------------------------------
917 static HSZ
DDEAddAtom(const wxString
& string
)
919 HSZ atom
= DDEAtomFromString(string
);
920 wxAtomTable
.Append(string
, (wxObject
*)atom
);
924 static HSZ
DDEGetAtom(const wxString
& string
)
926 wxNode
*node
= wxAtomTable
.Find(string
);
928 return (HSZ
)node
->Data();
932 return (HSZ
)(wxAtomTable
.Find(string
)->Data());
937 static HSZ
DDEAtomFromString(const wxString
& s
)
939 wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") );
941 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, (wxChar
*) s
.c_str(), DDE_CP
);
944 DDELogError(_("Failed to create DDE string"));
950 static wxString
DDEStringFromAtom(HSZ hsz
)
952 // all DDE strings are normally limited to 255 bytes
953 static const size_t len
= 256;
956 (void)DdeQueryString(DDEIdInst
, hsz
, s
.GetWriteBuf(len
), len
, DDE_CP
);
962 // ----------------------------------------------------------------------------
964 // ----------------------------------------------------------------------------
966 static void DDELogError(const wxString
& s
, UINT error
)
970 error
= DdeGetLastError(DDEIdInst
);
973 wxLogError(s
+ _T(": ") + DDEGetErrorMsg(error
));
976 static wxString
DDEGetErrorMsg(UINT error
)
981 case DMLERR_NO_ERROR
:
982 err
= _("no DDE error.");
985 case DMLERR_ADVACKTIMEOUT
:
986 err
= _("a request for a synchronous advise transaction has timed out.");
989 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
991 case DMLERR_DATAACKTIMEOUT
:
992 err
= _("a request for a synchronous data transaction has timed out.");
994 case DMLERR_DLL_NOT_INITIALIZED
:
995 err
= _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
997 case DMLERR_DLL_USAGE
:
998 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.");
1000 case DMLERR_EXECACKTIMEOUT
:
1001 err
= _("a request for a synchronous execute transaction has timed out.");
1003 case DMLERR_INVALIDPARAMETER
:
1004 err
= _("a parameter failed to be validated by the DDEML.");
1006 case DMLERR_LOW_MEMORY
:
1007 err
= _("a DDEML application has created a prolonged race condition.");
1009 case DMLERR_MEMORY_ERROR
:
1010 err
= _("a memory allocation failed.");
1012 case DMLERR_NO_CONV_ESTABLISHED
:
1013 err
= _("a client's attempt to establish a conversation has failed.");
1015 case DMLERR_NOTPROCESSED
:
1016 err
= _("a transaction failed.");
1018 case DMLERR_POKEACKTIMEOUT
:
1019 err
= _("a request for a synchronous poke transaction has timed out.");
1021 case DMLERR_POSTMSG_FAILED
:
1022 err
= _("an internal call to the PostMessage function has failed. ");
1024 case DMLERR_REENTRANCY
:
1025 err
= _("reentrancy problem.");
1027 case DMLERR_SERVER_DIED
:
1028 err
= _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1030 case DMLERR_SYS_ERROR
:
1031 err
= _("an internal error has occurred in the DDEML.");
1033 case DMLERR_UNADVACKTIMEOUT
:
1034 err
= _("a request to end an advise transaction has timed out.");
1036 case DMLERR_UNFOUND_QUEUE_ID
:
1037 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.");
1040 err
.Printf(_("Unknown DDE error %08x"), error
);