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 char *DDEDefaultIPCBuffer
= NULL
;
133 int DDEDefaultIPCBufferSize
= 0;
134 static bool DDEInitialized
= FALSE
;
136 // ----------------------------------------------------------------------------
138 // ----------------------------------------------------------------------------
140 // A module to allow DDE cleanup without calling these functions
141 // from app.cpp or from the user's application.
143 class wxDDEModule
: public wxModule
147 bool OnInit() { return TRUE
; }
148 void OnExit() { wxDDECleanUp(); }
151 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
154 // ----------------------------------------------------------------------------
156 // ----------------------------------------------------------------------------
158 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
159 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
160 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
161 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
163 // ============================================================================
165 // ============================================================================
167 // ----------------------------------------------------------------------------
168 // initialization and cleanup
169 // ----------------------------------------------------------------------------
171 extern void wxDDEInitialize()
173 if ( !DDEInitialized
)
175 // Should insert filter flags
176 PFNCALLBACK callback
= (PFNCALLBACK
)
177 MakeProcInstance((FARPROC
)_DDECallback
, wxGetInstance());
178 UINT rc
= DdeInitialize(&DDEIdInst
, callback
, APPCLASS_STANDARD
, 0L);
179 if ( rc
!= DMLERR_NO_ERROR
)
181 DDELogError(_T("Failed to initialize DDE"), rc
);
185 DDEInitialized
= TRUE
;
192 if ( DDEIdInst
!= 0 )
194 DdeUninitialize(DDEIdInst
);
198 delete [] DDEDefaultIPCBuffer
;
201 // ----------------------------------------------------------------------------
202 // functions working with the global connection list(s)
203 // ----------------------------------------------------------------------------
205 // Global find connection
206 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
208 wxNode
*node
= wxDDEServerObjects
.First();
209 wxDDEConnection
*found
= NULL
;
210 while (node
&& !found
)
212 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
213 found
= object
->FindConnection((WXHCONV
) hConv
);
219 node
= wxDDEClientObjects
.First();
220 while (node
&& !found
)
222 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
223 found
= object
->FindConnection((WXHCONV
) hConv
);
229 // Global delete connection
230 static void DDEDeleteConnection(HCONV hConv
)
232 wxNode
*node
= wxDDEServerObjects
.First();
234 while (node
&& !found
)
236 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
237 found
= object
->DeleteConnection((WXHCONV
) hConv
);
243 node
= wxDDEClientObjects
.First();
244 while (node
&& !found
)
246 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
247 found
= object
->DeleteConnection((WXHCONV
) hConv
);
252 // Find a server from a service name
253 static wxDDEServer
*DDEFindServer(const wxString
& s
)
255 wxNode
*node
= wxDDEServerObjects
.First();
256 wxDDEServer
*found
= NULL
;
257 while (node
&& !found
)
259 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
261 if (object
->GetServiceName() == s
)
263 else node
= node
->Next();
268 // ----------------------------------------------------------------------------
270 // ----------------------------------------------------------------------------
272 wxDDEServer::wxDDEServer()
276 wxDDEServerObjects
.Append(this);
279 bool wxDDEServer::Create(const wxString
& server
)
281 m_serviceName
= server
;
283 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(server
), (HSZ
)NULL
, DNS_REGISTER
) )
285 DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
294 wxDDEServer::~wxDDEServer()
296 if ( !!m_serviceName
)
298 if ( !DdeNameService(DDEIdInst
, DDEAtomFromString(m_serviceName
),
299 (HSZ
)NULL
, DNS_UNREGISTER
) )
301 DDELogError(wxString::Format(_("Failed to unregister DDE server '%s'"),
302 m_serviceName
.c_str()));
306 wxDDEServerObjects
.DeleteObject(this);
308 wxNode
*node
= m_connections
.First();
311 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
312 wxNode
*next
= node
->Next();
313 connection
->OnDisconnect(); // May delete the node implicitly
317 // If any left after this, delete them
318 node
= m_connections
.First();
321 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
322 wxNode
*next
= node
->Next();
328 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
330 return new wxDDEConnection
;
333 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
335 wxNode
*node
= m_connections
.First();
336 wxDDEConnection
*found
= NULL
;
337 while (node
&& !found
)
339 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
340 if (connection
->m_hConv
== conv
)
342 else node
= node
->Next();
347 // Only delete the entry in the map, not the actual connection
348 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
350 wxNode
*node
= m_connections
.First();
352 while (node
&& !found
)
354 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
355 if (connection
->m_hConv
== conv
)
360 else node
= node
->Next();
365 // ----------------------------------------------------------------------------
367 // ----------------------------------------------------------------------------
369 wxDDEClient::wxDDEClient()
373 wxDDEClientObjects
.Append(this);
376 wxDDEClient::~wxDDEClient()
378 wxDDEClientObjects
.DeleteObject(this);
379 wxNode
*node
= m_connections
.First();
382 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
383 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
384 node
= m_connections
.First();
388 bool wxDDEClient::ValidHost(const wxString
& /* host */)
393 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& WXUNUSED(host
),
394 const wxString
& server
,
395 const wxString
& topic
)
397 HCONV hConv
= DdeConnect(DDEIdInst
, DDEAtomFromString(server
), DDEAtomFromString(topic
),
401 DDELogError(wxString::Format(_("Failed to create connection to "
402 "server '%s' on topic '%s'"),
403 server
.c_str(), topic
.c_str()));
407 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
410 connection
->m_hConv
= (WXHCONV
) hConv
;
411 connection
->m_topicName
= topic
;
412 connection
->m_client
= this;
413 m_connections
.Append(connection
);
418 return (wxConnectionBase
*) NULL
;
421 wxConnectionBase
*wxDDEClient::OnMakeConnection()
423 return new wxDDEConnection
;
426 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
428 wxNode
*node
= m_connections
.First();
429 wxDDEConnection
*found
= NULL
;
430 while (node
&& !found
)
432 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
433 if (connection
->m_hConv
== conv
)
435 else node
= node
->Next();
440 // Only delete the entry in the map, not the actual connection
441 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
443 wxNode
*node
= m_connections
.First();
445 while (node
&& !found
)
447 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
448 if (connection
->m_hConv
== conv
)
453 else node
= node
->Next();
458 // ----------------------------------------------------------------------------
460 // ----------------------------------------------------------------------------
462 wxDDEConnection::wxDDEConnection(char *buffer
, int size
)
466 if (DDEDefaultIPCBuffer
== NULL
)
467 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
468 m_bufPtr
= DDEDefaultIPCBuffer
;
469 m_bufSize
= DDEDefaultIPCBufferSize
;
481 m_sendingData
= NULL
;
484 wxDDEConnection::wxDDEConnection()
487 m_sendingData
= NULL
;
490 if (DDEDefaultIPCBuffer
== NULL
)
491 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
493 m_bufPtr
= DDEDefaultIPCBuffer
;
494 m_bufSize
= DDEDefaultIPCBufferSize
;
497 wxDDEConnection::~wxDDEConnection()
500 m_server
->GetConnections().DeleteObject(this);
502 m_client
->GetConnections().DeleteObject(this);
505 // Calls that CLIENT can make
506 bool wxDDEConnection::Disconnect()
508 DDEDeleteConnection(GetHConv());
510 bool ok
= DdeDisconnect(GetHConv()) != 0;
513 DDELogError(_T("Failed to disconnect from DDE server gracefully"));
519 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
524 size
= wxStrlen(data
) + 1;
527 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
536 DDELogError(_T("DDE execute request failed"));
542 char *wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
545 HSZ atom
= DDEGetAtom(item
);
547 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0,
553 if ( !returned_data
)
555 DDELogError(_T("DDE data request failed"));
560 DWORD len
= DdeGetData(returned_data
, (LPBYTE
)m_bufPtr
, m_bufSize
, 0);
562 DdeFreeDataHandle(returned_data
);
570 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
575 size
= wxStrlen(data
) + 1;
578 HSZ item_atom
= DDEGetAtom(item
);
579 bool ok
= DdeClientTransaction((LPBYTE
)data
, size
,
587 DDELogError(_("DDE poke request failed"));
593 bool wxDDEConnection::StartAdvise(const wxString
& item
)
596 HSZ atom
= DDEGetAtom(item
);
598 bool ok
= DdeClientTransaction(NULL
, 0,
606 DDELogError(_("Failed to establish an advise loop with DDE server"));
612 bool wxDDEConnection::StopAdvise(const wxString
& item
)
615 HSZ atom
= DDEGetAtom(item
);
617 bool ok
= DdeClientTransaction(NULL
, 0,
625 DDELogError(_("Failed to terminate the advise loop with DDE server"));
631 // Calls that SERVER can make
632 bool wxDDEConnection::Advise(const wxString
& item
,
639 size
= wxStrlen(data
) + 1;
642 HSZ item_atom
= DDEGetAtom(item
);
643 HSZ topic_atom
= DDEGetAtom(m_topicName
);
644 m_sendingData
= data
;
648 bool ok
= DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0;
651 DDELogError(_("Failed to send DDE advise notification"));
657 bool wxDDEConnection::OnDisconnect()
663 // ----------------------------------------------------------------------------
665 // ----------------------------------------------------------------------------
667 #define DDERETURN HDDEDATA
669 HDDEDATA EXPENTRY _EXPORT
670 _DDECallback(WORD wType
,
676 DWORD
WXUNUSED(lData1
),
677 DWORD
WXUNUSED(lData2
))
683 wxString topic
= DDEStringFromAtom(hsz1
),
684 srv
= DDEStringFromAtom(hsz2
);
685 wxDDEServer
*server
= DDEFindServer(srv
);
688 wxDDEConnection
*connection
=
689 (wxDDEConnection
*) server
->OnAcceptConnection(topic
);
692 connection
->m_server
= server
;
693 server
->GetConnections().Append(connection
);
694 connection
->m_hConv
= 0;
695 connection
->m_topicName
= topic
;
696 DDECurrentlyConnecting
= connection
;
697 return (DDERETURN
)(DWORD
)TRUE
;
703 case XTYP_CONNECT_CONFIRM
:
705 if (DDECurrentlyConnecting
)
707 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
708 DDECurrentlyConnecting
= NULL
;
709 return (DDERETURN
)(DWORD
)TRUE
;
714 case XTYP_DISCONNECT
:
716 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
717 if (connection
&& connection
->OnDisconnect())
719 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
720 return (DDERETURN
)(DWORD
)TRUE
;
727 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
731 DWORD len
= DdeGetData(hData
,
732 (LPBYTE
)connection
->m_bufPtr
,
733 connection
->m_bufSize
,
735 DdeFreeDataHandle(hData
);
736 if ( connection
->OnExecute(connection
->m_topicName
,
737 connection
->m_bufPtr
,
739 (wxIPCFormat
) wFmt
) )
741 return (DDERETURN
)(DWORD
)DDE_FACK
;
745 return (DDERETURN
)DDE_FNOTPROCESSED
;
750 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
754 wxString item_name
= DDEStringFromAtom(hsz2
);
757 char *data
= connection
->OnRequest(connection
->m_topicName
,
764 user_size
= wxStrlen(data
) + 1;
766 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
773 return (DDERETURN
)handle
;
781 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
785 wxString item_name
= DDEStringFromAtom(hsz2
);
787 DWORD len
= DdeGetData(hData
,
788 (LPBYTE
)connection
->m_bufPtr
,
789 connection
->m_bufSize
,
791 DdeFreeDataHandle(hData
);
793 connection
->OnPoke(connection
->m_topicName
,
795 connection
->m_bufPtr
,
799 return (DDERETURN
)DDE_FACK
;
803 return (DDERETURN
)DDE_FNOTPROCESSED
;
809 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
813 wxString item_name
= DDEStringFromAtom(hsz2
);
815 return (DDERETURN
)connection
->
816 OnStartAdvise(connection
->m_topicName
, item_name
);
824 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
828 wxString item_name
= DDEStringFromAtom(hsz2
);
830 return (DDERETURN
)connection
->
831 OnStopAdvise(connection
->m_topicName
, item_name
);
839 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
841 if (connection
&& connection
->m_sendingData
)
843 HDDEDATA data
= DdeCreateDataHandle
846 (LPBYTE
)connection
->m_sendingData
,
847 connection
->m_dataSize
,
850 connection
->m_dataType
,
854 connection
->m_sendingData
= NULL
;
856 return (DDERETURN
)data
;
864 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
868 wxString item_name
= DDEStringFromAtom(hsz2
);
870 DWORD len
= DdeGetData(hData
,
871 (LPBYTE
)connection
->m_bufPtr
,
872 connection
->m_bufSize
,
874 DdeFreeDataHandle(hData
);
875 if ( connection
->OnAdvise(connection
->m_topicName
,
877 connection
->m_bufPtr
,
879 (wxIPCFormat
) wFmt
) )
881 return (DDERETURN
)(DWORD
)DDE_FACK
;
885 return (DDERETURN
)DDE_FNOTPROCESSED
;
892 // ----------------------------------------------------------------------------
893 // DDE strings and atoms
894 // ----------------------------------------------------------------------------
897 static HSZ
DDEAddAtom(const wxString
& string
)
899 HSZ atom
= DDEAtomFromString(string
);
900 wxAtomTable
.Append(string
, (wxObject
*)atom
);
904 static HSZ
DDEGetAtom(const wxString
& string
)
906 wxNode
*node
= wxAtomTable
.Find(string
);
908 return (HSZ
)node
->Data();
912 return (HSZ
)(wxAtomTable
.Find(string
)->Data());
917 static HSZ
DDEAtomFromString(const wxString
& s
)
919 wxASSERT_MSG( DDEIdInst
, _T("DDE not initialized") );
921 HSZ hsz
= DdeCreateStringHandle(DDEIdInst
, (char*) s
.c_str(), DDE_CP
);
924 DDELogError(_("Failed to create DDE string"));
930 static wxString
DDEStringFromAtom(HSZ hsz
)
932 // all DDE strings are normally limited to 255 bytes
933 static const size_t len
= 256;
936 (void)DdeQueryString(DDEIdInst
, hsz
, s
.GetWriteBuf(len
), len
, DDE_CP
);
942 // ----------------------------------------------------------------------------
944 // ----------------------------------------------------------------------------
946 static void DDELogError(const wxString
& s
, UINT error
)
950 error
= DdeGetLastError(DDEIdInst
);
953 wxLogError(s
+ _T(": ") + DDEGetErrorMsg(error
));
956 static wxString
DDEGetErrorMsg(UINT error
)
961 case DMLERR_NO_ERROR
:
962 err
= _("no DDE error.");
965 case DMLERR_ADVACKTIMEOUT
:
966 err
= _("a request for a synchronous advise transaction has timed out.");
969 err
= _("the response to the transaction caused the DDE_FBUSY bit to be set.");
971 case DMLERR_DATAACKTIMEOUT
:
972 err
= _("a request for a synchronous data transaction has timed out.");
974 case DMLERR_DLL_NOT_INITIALIZED
:
975 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.");
977 case DMLERR_DLL_USAGE
:
978 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.");
980 case DMLERR_EXECACKTIMEOUT
:
981 err
= _("a request for a synchronous execute transaction has timed out.");
983 case DMLERR_INVALIDPARAMETER
:
984 err
= _("a parameter failed to be validated by the DDEML.");
986 case DMLERR_LOW_MEMORY
:
987 err
= _("a DDEML application has created a prolonged race condition.");
989 case DMLERR_MEMORY_ERROR
:
990 err
= _("a memory allocation failed.");
992 case DMLERR_NO_CONV_ESTABLISHED
:
993 err
= _("a client's attempt to establish a conversation has failed.");
995 case DMLERR_NOTPROCESSED
:
996 err
= _("a transaction failed.");
998 case DMLERR_POKEACKTIMEOUT
:
999 err
= _("a request for a synchronous poke transaction has timed out.");
1001 case DMLERR_POSTMSG_FAILED
:
1002 err
= _("an internal call to the PostMessage function has failed. ");
1004 case DMLERR_REENTRANCY
:
1005 err
= _("reentrancy problem.");
1007 case DMLERR_SERVER_DIED
:
1008 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.");
1010 case DMLERR_SYS_ERROR
:
1011 err
= _("an internal error has occurred in the DDEML.");
1013 case DMLERR_UNADVACKTIMEOUT
:
1014 err
= _("a request to end an advise transaction has timed out.");
1016 case DMLERR_UNFOUND_QUEUE_ID
:
1017 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.");
1020 err
.Printf(_("Unknown DDE error %08x"), error
);