1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: DDE classes
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
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/hashmap.h"
43 #include "wx/msw/private.h"
49 #ifdef __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 WX_DECLARE_STRING_HASH_MAP( HSZ
, wxAtomMap
);
127 static DWORD DDEIdInst
= 0L;
128 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
129 static wxAtomMap wxAtomTable
;
131 #include "wx/listimpl.cpp"
133 WX_DEFINE_LIST(wxDDEClientList
);
134 WX_DEFINE_LIST(wxDDEServerList
);
135 WX_DEFINE_LIST(wxDDEConnectionList
);
137 static wxDDEClientList wxDDEClientObjects
;
138 static wxDDEServerList wxDDEServerObjects
;
140 static bool DDEInitialized
= false;
142 // ----------------------------------------------------------------------------
144 // ----------------------------------------------------------------------------
146 // A module to allow DDE cleanup without calling these functions
147 // from app.cpp or from the user's application.
149 class wxDDEModule
: public wxModule
153 bool OnInit() { return true; }
154 void OnExit() { wxDDECleanUp(); }
157 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
160 // ----------------------------------------------------------------------------
162 // ----------------------------------------------------------------------------
164 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
165 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
166 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
167 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
169 // ============================================================================
171 // ============================================================================
173 // ----------------------------------------------------------------------------
174 // initialization and cleanup
175 // ----------------------------------------------------------------------------
177 extern void wxDDEInitialize()
179 if ( !DDEInitialized
)
181 // Should insert filter flags
182 PFNCALLBACK callback
= (PFNCALLBACK
)
183 MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance());
184 UINT rc
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L);
185 if ( rc
!= DMLERR_NO_ERROR
)
187 DDELogError(_T("Failed to initialize DDE"), rc
);
191 DDEInitialized
= true;
198 WX_CLEAR_LIST(wxDDEClientList
, wxDDEClientObjects
);
199 WX_CLEAR_LIST(wxDDEServerList
, wxDDEServerObjects
);
203 if ( DDEIdInst
!= 0 )
205 DdeUninitialize(DDEIdInst
);
210 // ----------------------------------------------------------------------------
211 // functions working with the global connection list(s)
212 // ----------------------------------------------------------------------------
214 // Global find connection
215 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
217 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
218 wxDDEConnection
*found
= NULL
;
219 while (serverNode
&& !found
)
221 wxDDEServer
*object
= serverNode
->GetData();
222 found
= object
->FindConnection((WXHCONV
) hConv
);
223 serverNode
= serverNode
->GetNext();
231 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
232 while (clientNode
&& !found
)
234 wxDDEClient
*object
= clientNode
->GetData();
235 found
= object
->FindConnection((WXHCONV
) hConv
);
236 clientNode
= clientNode
->GetNext();
241 // Global delete connection
242 static void DDEDeleteConnection(HCONV hConv
)
244 wxDDEServerList::compatibility_iterator serverNode
= wxDDEServerObjects
.GetFirst();
246 while (serverNode
&& !found
)
248 wxDDEServer
*object
= serverNode
->GetData();
249 found
= object
->DeleteConnection((WXHCONV
) hConv
);
250 serverNode
= serverNode
->GetNext();
257 wxDDEClientList::compatibility_iterator clientNode
= wxDDEClientObjects
.GetFirst();
258 while (clientNode
&& !found
)
260 wxDDEClient
*object
= clientNode
->GetData();
261 found
= object
->DeleteConnection((WXHCONV
) hConv
);
262 clientNode
= clientNode
->GetNext();
266 // Find a server from a service name
267 static wxDDEServer
*DDEFindServer(const wxString
& s
)
269 wxDDEServerList::compatibility_iterator node
= wxDDEServerObjects
.GetFirst();
270 wxDDEServer
*found
= NULL
;
271 while (node
&& !found
)
273 wxDDEServer
*object
= node
->GetData();
275 if (object
->GetServiceName() == s
)
281 node
= node
->GetNext();
288 // ----------------------------------------------------------------------------
290 // ----------------------------------------------------------------------------
292 wxDDEServer::wxDDEServer()
296 wxDDEServerObjects
.Append(this);
299 bool wxDDEServer::Create(const wxString
& server
)
301 m_serviceName
= server
;
303 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(server
), (HSZ
)NULL
, DNS_REGISTER
) )
305 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
314 wxDDEServer::~wxDDEServer()
316 if ( !!m_serviceName
)
318 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(m_serviceName
),
319 (HSZ
)NULL
, DNS_UNREGISTER
) )
321 DDELogError(wxString::Format(_("Failed to unregister DDE server '%s'"),
322 m_serviceName
.c_str()));
326 wxDDEServerObjects
.DeleteObject(this);
328 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
331 wxDDEConnection
*connection
= node
->GetData();
332 wxDDEConnectionList::compatibility_iterator next
= node
->GetNext();
333 connection
->SetConnected(false);
334 connection
->OnDisconnect(); // May delete the node implicitly
338 // If any left after this, delete them
339 node
= m_connections
.GetFirst();
342 wxDDEConnection
*connection
= node
->GetData();
343 wxDDEConnectionList::compatibility_iterator next
= node
->GetNext();
349 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
351 return new wxDDEConnection
;
354 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
356 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
357 wxDDEConnection
*found
= NULL
;
358 while (node
&& !found
)
360 wxDDEConnection
*connection
= node
->GetData();
361 if (connection
->m_hConv
== conv
)
363 else node
= node
->GetNext();
368 // Only delete the entry in the map, not the actual connection
369 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
371 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
374 wxDDEConnection
*connection
= node
->GetData();
375 if (connection
->m_hConv
== conv
)
377 m_connections
.Erase(node
);
382 node
= node
->GetNext();
388 // ----------------------------------------------------------------------------
390 // ----------------------------------------------------------------------------
392 wxDDEClient::wxDDEClient()
396 wxDDEClientObjects
.Append(this);
399 wxDDEClient::~wxDDEClient()
401 wxDDEClientObjects
.DeleteObject(this);
402 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
405 wxDDEConnection
*connection
= node
->GetData();
406 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
407 node
= m_connections
.GetFirst();
411 bool wxDDEClient::ValidHost(const wxString
& /* host */)
416 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
),
417 const wxString
& server
,
418 const wxString
& topic
)
420 HCONV hConv
= DdeConnect(DDEIdInst
, DDEAtomFromString(server
), DDEAtomFromString(topic
),
424 DDELogError(wxString::Format(_("Failed to create connection to server '%s' on topic '%s'"),
425 server
.c_str(), topic
.c_str()));
429 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
432 connection
->m_hConv
= (WXHCONV
) hConv
;
433 connection
->m_topicName
= topic
;
434 connection
->m_client
= this;
435 m_connections
.Append(connection
);
440 return (wxConnectionBase
*) NULL
;
443 wxConnectionBase
*wxDDEClient::OnMakeConnection()
445 return new wxDDEConnection
;
448 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
450 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
451 wxDDEConnection
*found
= NULL
;
452 while (node
&& !found
)
454 wxDDEConnection
*connection
= node
->GetData();
455 if (connection
->m_hConv
== conv
)
457 else node
= node
->GetNext();
462 // Only delete the entry in the map, not the actual connection
463 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
465 wxDDEConnectionList::compatibility_iterator node
= m_connections
.GetFirst();
468 wxDDEConnection
*connection
= node
->GetData();
469 if (connection
->m_hConv
== conv
)
471 m_connections
.Erase(node
);
474 else node
= node
->GetNext();
479 // ----------------------------------------------------------------------------
481 // ----------------------------------------------------------------------------
483 wxDDEConnection::wxDDEConnection(wxChar
*buffer
, int size
)
484 : wxConnectionBase(buffer
, size
)
490 m_sendingData
= NULL
;
493 wxDDEConnection::wxDDEConnection()
497 m_sendingData
= NULL
;
502 wxDDEConnection::~wxDDEConnection()
506 m_server
->GetConnections().DeleteObject(this);
508 m_client
->GetConnections().DeleteObject(this);
511 // Calls that CLIENT can make
512 bool wxDDEConnection::Disconnect()
514 if ( !GetConnected() )
517 DDEDeleteConnection(GetHConv());
519 bool ok
= DdeDisconnect(GetHConv()) != 0;
522 DDELogError(_T("Failed to disconnect from DDE server gracefully"));
525 SetConnected( false ); // so we don't try and disconnect again
530 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
535 size
= wxStrlen(data
) + 1;
538 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
548 DDELogError(_T("DDE execute request failed"));
554 wxChar
*wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
558 HSZ atom
= DDEGetAtom(item
);
560 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
566 if ( !returned_data
)
568 DDELogError(_T("DDE data request failed"));
573 DWORD len
= DdeGetData(returned_data
, NULL
, 0, 0);
575 wxChar
*data
= GetBufferAtLeast( len
);
576 wxASSERT_MSG(data
!= NULL
,
577 _T("Buffer too small in wxDDEConnection::Request") );
578 DdeGetData(returned_data
, (LPBYTE
)data
, len
, 0);
580 DdeFreeDataHandle(returned_data
);
588 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
593 size
= wxStrlen(data
) + 1;
596 HSZ item_atom
= DDEGetAtom(item
);
597 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
605 DDELogError(_("DDE poke request failed"));
611 bool wxDDEConnection::StartAdvise(const wxString
& item
)
614 HSZ atom
= DDEGetAtom(item
);
616 bool ok
= DdeClientTransaction(NULL
, 0,
624 DDELogError(_("Failed to establish an advise loop with DDE server"));
630 bool wxDDEConnection::StopAdvise(const wxString
& item
)
633 HSZ atom
= DDEGetAtom(item
);
635 bool ok
= DdeClientTransaction(NULL
, 0,
643 DDELogError(_("Failed to terminate the advise loop with DDE server"));
649 // Calls that SERVER can make
650 bool wxDDEConnection::Advise(const wxString
& item
,
657 size
= wxStrlen(data
) + 1;
660 HSZ item_atom
= DDEGetAtom(item
);
661 HSZ topic_atom
= DDEGetAtom(m_topicName
);
662 m_sendingData
= data
; // mrf: potential for scope problems here?
666 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
669 DDELogError(_("Failed to send DDE advise notification"));
675 bool wxDDEConnection::OnDisconnect()
681 // ----------------------------------------------------------------------------
683 // ----------------------------------------------------------------------------
685 #define DDERETURN HDDEDATA
687 HDDEDATA EXPENTRY _EXPORT
688 _DDECallback(WORD wType
,
694 DWORD
WXUNUSED(lData1
),
695 DWORD
WXUNUSED(lData2
))
701 wxString topic
= DDEStringFromAtom(hsz1
),
702 srv
= DDEStringFromAtom(hsz2
);
703 wxDDEServer
*server
= DDEFindServer(srv
);
706 wxDDEConnection
*connection
=
707 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
710 connection
->m_server
= server
;
711 server
->GetConnections().Append(connection
);
712 connection
->m_hConv
= 0;
713 connection
->m_topicName
= topic
;
714 DDECurrentlyConnecting
= connection
;
715 return (DDERETURN
)(DWORD
)true;
721 case XTYP_CONNECT_CONFIRM
:
723 if (DDECurrentlyConnecting
)
725 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
726 DDECurrentlyConnecting
= NULL
;
727 return (DDERETURN
)(DWORD
)true;
732 case XTYP_DISCONNECT
:
734 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
737 connection
->SetConnected( false );
738 if (connection
->OnDisconnect())
740 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
741 return (DDERETURN
)(DWORD
)true;
749 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
753 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
755 wxChar
*data
= connection
->GetBufferAtLeast( len
);
756 wxASSERT_MSG(data
!= NULL
,
757 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
759 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
761 DdeFreeDataHandle(hData
);
763 if ( connection
->OnExecute(connection
->m_topicName
,
766 (wxIPCFormat
) wFmt
) )
768 return (DDERETURN
)(DWORD
)DDE_FACK
;
772 return (DDERETURN
)DDE_FNOTPROCESSED
;
777 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
781 wxString item_name
= DDEStringFromAtom(hsz2
);
784 wxChar
*data
= connection
->OnRequest(connection
->m_topicName
,
791 user_size
= wxStrlen((wxChar
*)data
) + 1;
793 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
800 return (DDERETURN
)handle
;
808 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
812 wxString item_name
= DDEStringFromAtom(hsz2
);
814 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
816 wxChar
*data
= connection
->GetBufferAtLeast( len
);
817 wxASSERT_MSG(data
!= NULL
,
818 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
820 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
822 DdeFreeDataHandle(hData
);
824 connection
->OnPoke(connection
->m_topicName
,
830 return (DDERETURN
)DDE_FACK
;
834 return (DDERETURN
)DDE_FNOTPROCESSED
;
840 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
844 wxString item_name
= DDEStringFromAtom(hsz2
);
846 return (DDERETURN
)connection
->
847 OnStartAdvise(connection
->m_topicName
, item_name
);
855 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
859 wxString item_name
= DDEStringFromAtom(hsz2
);
861 return (DDERETURN
)connection
->
862 OnStopAdvise(connection
->m_topicName
, item_name
);
870 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
872 if (connection
&& connection
->m_sendingData
)
874 HDDEDATA data
= DdeCreateDataHandle
877 (LPBYTE
)connection
->m_sendingData
,
878 connection
->m_dataSize
,
881 connection
->m_dataType
,
885 connection
->m_sendingData
= NULL
;
887 return (DDERETURN
)data
;
895 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
899 wxString item_name
= DDEStringFromAtom(hsz2
);
901 DWORD len
= DdeGetData(hData
, NULL
, 0, 0);
903 wxChar
*data
= connection
->GetBufferAtLeast( len
);
904 wxASSERT_MSG(data
!= NULL
,
905 _T("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
907 DdeGetData(hData
, (LPBYTE
)data
, len
, 0);
909 DdeFreeDataHandle(hData
);
910 if ( connection
->OnAdvise(connection
->m_topicName
,
914 (wxIPCFormat
) wFmt
) )
916 return (DDERETURN
)(DWORD
)DDE_FACK
;
920 return (DDERETURN
)DDE_FNOTPROCESSED
;
927 // ----------------------------------------------------------------------------
928 // DDE strings and atoms
929 // ----------------------------------------------------------------------------
932 static HSZ
DDEAddAtom(const wxString
& str
)
934 HSZ atom
= DDEAtomFromString(str
);
935 wxAtomTable
[str
] = atom
;
939 static HSZ
DDEGetAtom(const wxString
& str
)
941 wxAtomMap::iterator it
= wxAtomTable
.find(str
);
943 if (it
!= wxAtomTable
.end())
946 return DDEAddAtom(str
);
950 static HSZ
DDEAtomFromString(const wxString
& s
)
952 wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") );
954 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, (wxChar
*) s
.c_str(), DDE_CP
);
957 DDELogError(_("Failed to create DDE string"));
963 static wxString
DDEStringFromAtom(HSZ hsz
)
965 // all DDE strings are normally limited to 255 bytes
966 static const size_t len
= 256;
969 (void)DdeQueryString(DDEIdInst
, hsz
, s
.GetWriteBuf(len
), len
, DDE_CP
);
975 // ----------------------------------------------------------------------------
977 // ----------------------------------------------------------------------------
979 static void DDELogError(const wxString
& s
, UINT error
)
983 error
= DdeGetLastError(DDEIdInst
);
986 wxLogError(s
+ _T(": ") + DDEGetErrorMsg(error
));
989 static wxString
DDEGetErrorMsg(UINT error
)
994 case DMLERR_NO_ERROR
:
995 err
= _("no DDE error.");
998 case DMLERR_ADVACKTIMEOUT
:
999 err
= _("a request for a synchronous advise transaction has timed out.");
1002 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
1004 case DMLERR_DATAACKTIMEOUT
:
1005 err
= _("a request for a synchronous data transaction has timed out.");
1007 case DMLERR_DLL_NOT_INITIALIZED
:
1008 err
= _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
1010 case DMLERR_DLL_USAGE
:
1011 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.");
1013 case DMLERR_EXECACKTIMEOUT
:
1014 err
= _("a request for a synchronous execute transaction has timed out.");
1016 case DMLERR_INVALIDPARAMETER
:
1017 err
= _("a parameter failed to be validated by the DDEML.");
1019 case DMLERR_LOW_MEMORY
:
1020 err
= _("a DDEML application has created a prolonged race condition.");
1022 case DMLERR_MEMORY_ERROR
:
1023 err
= _("a memory allocation failed.");
1025 case DMLERR_NO_CONV_ESTABLISHED
:
1026 err
= _("a client's attempt to establish a conversation has failed.");
1028 case DMLERR_NOTPROCESSED
:
1029 err
= _("a transaction failed.");
1031 case DMLERR_POKEACKTIMEOUT
:
1032 err
= _("a request for a synchronous poke transaction has timed out.");
1034 case DMLERR_POSTMSG_FAILED
:
1035 err
= _("an internal call to the PostMessage function has failed. ");
1037 case DMLERR_REENTRANCY
:
1038 err
= _("reentrancy problem.");
1040 case DMLERR_SERVER_DIED
:
1041 err
= _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1043 case DMLERR_SYS_ERROR
:
1044 err
= _("an internal error has occurred in the DDEML.");
1046 case DMLERR_UNADVACKTIMEOUT
:
1047 err
= _("a request to end an advise transaction has timed out.");
1049 case DMLERR_UNFOUND_QUEUE_ID
:
1050 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.");
1053 err
.Printf(_("Unknown DDE error %08x"), error
);