1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: DDE classes
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "dde.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
34 #include "wx/msw/private.h"
39 #include "wx/msw/gnuwin32/extra.h"
50 #define _EXPORT _export
53 #if !USE_SHARED_LIBRARY
54 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
55 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
56 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
59 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
60 static void DDEDeleteConnection(HCONV hConv
);
61 static wxDDEServer
*DDEFindServer(const wxString
& s
);
63 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(
73 // Add topic name to atom table before using in conversations
74 static HSZ
DDEAddAtom(const wxString
& string
);
75 static HSZ
DDEGetAtom(const wxString
& string
);
76 static void DDEPrintError(void);
78 static DWORD DDEIdInst
= 0L;
79 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
81 static wxList
wxAtomTable(wxKEY_STRING
);
82 static wxList wxDDEClientObjects
;
83 static wxList wxDDEServerObjects
;
85 char *DDEDefaultIPCBuffer
= NULL
;
86 int DDEDefaultIPCBufferSize
= 0;
93 static bool DDEInitialized
= FALSE
;
95 void wxDDEInitialize()
99 DDEInitialized
= TRUE
;
101 // Should insert filter flags
102 DdeInitialize(&DDEIdInst
, (PFNCALLBACK
)MakeProcInstance(
103 (FARPROC
)_DDECallback
, wxGetInstance()),
116 DdeUninitialize(DDEIdInst
);
119 if (DDEDefaultIPCBuffer
)
120 delete [] DDEDefaultIPCBuffer
;
123 // Global find connection
124 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
126 wxNode
*node
= wxDDEServerObjects
.First();
127 wxDDEConnection
*found
= NULL
;
128 while (node
&& !found
)
130 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
131 found
= object
->FindConnection((WXHCONV
) hConv
);
137 node
= wxDDEClientObjects
.First();
138 while (node
&& !found
)
140 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
141 found
= object
->FindConnection((WXHCONV
) hConv
);
147 // Global delete connection
148 static void DDEDeleteConnection(HCONV hConv
)
150 wxNode
*node
= wxDDEServerObjects
.First();
152 while (node
&& !found
)
154 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
155 found
= object
->DeleteConnection((WXHCONV
) hConv
);
161 node
= wxDDEServerObjects
.First();
162 while (node
&& !found
)
164 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
165 found
= object
->DeleteConnection((WXHCONV
) hConv
);
170 // Find a server from a service name
171 static wxDDEServer
*DDEFindServer(const wxString
& s
)
173 wxNode
*node
= wxDDEServerObjects
.First();
174 wxDDEServer
*found
= NULL
;
175 while (node
&& !found
)
177 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
179 if (object
->GetServiceName() == s
)
181 else node
= node
->Next();
191 wxDDEServer::wxDDEServer(void)
194 wxDDEServerObjects
.Append(this);
197 bool wxDDEServer::Create(const wxString
& server_name
)
199 m_serviceName
= server_name
;
200 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)server_name
, CP_WINANSI
);
202 if (DdeNameService(DDEIdInst
, serviceName
, (HSZ
) NULL
, DNS_REGISTER
) == 0)
210 wxDDEServer::~wxDDEServer(void)
212 if (m_serviceName
!= "")
214 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)m_serviceName
, CP_WINANSI
);
215 if (DdeNameService(DDEIdInst
, serviceName
, NULL
, DNS_UNREGISTER
) == 0)
220 wxDDEServerObjects
.DeleteObject(this);
222 wxNode
*node
= m_connections
.First();
225 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
226 wxNode
*next
= node
->Next();
227 connection
->OnDisconnect(); // May delete the node implicitly
231 // If any left after this, delete them
232 node
= m_connections
.First();
235 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
236 wxNode
*next
= node
->Next();
242 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
244 return new wxDDEConnection
;
247 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
249 wxNode
*node
= m_connections
.First();
250 wxDDEConnection
*found
= NULL
;
251 while (node
&& !found
)
253 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
254 if (connection
->m_hConv
== conv
)
256 else node
= node
->Next();
261 // Only delete the entry in the map, not the actual connection
262 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
264 wxNode
*node
= m_connections
.First();
266 while (node
&& !found
)
268 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
269 if (connection
->m_hConv
== conv
)
274 else node
= node
->Next();
286 wxDDEClient::wxDDEClient(void)
288 wxDDEClientObjects
.Append(this);
291 wxDDEClient::~wxDDEClient(void)
293 wxDDEClientObjects
.DeleteObject(this);
294 wxNode
*node
= m_connections
.First();
297 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
298 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
299 node
= m_connections
.First();
303 bool wxDDEClient::ValidHost(const wxString
& /* host */)
308 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& /* host */, const wxString
& server_name
, const wxString
& topic
)
310 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)server_name
, CP_WINANSI
);
311 HSZ topic_atom
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)topic
, CP_WINANSI
);
313 HCONV hConv
= DdeConnect(DDEIdInst
, serviceName
, topic_atom
, (PCONVCONTEXT
)NULL
);
314 if (hConv
== (HCONV
) NULL
)
315 return (wxConnectionBase
*) NULL
;
318 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
321 connection
->m_hConv
= (WXHCONV
) hConv
;
322 connection
->m_topicName
= topic
;
323 connection
->m_client
= this;
324 m_connections
.Append(connection
);
327 else return (wxConnectionBase
*) NULL
;
331 wxConnectionBase
*wxDDEClient::OnMakeConnection(void)
333 return new wxDDEConnection
;
336 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
338 wxNode
*node
= m_connections
.First();
339 wxDDEConnection
*found
= NULL
;
340 while (node
&& !found
)
342 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
343 if (connection
->m_hConv
== conv
)
345 else node
= node
->Next();
350 // Only delete the entry in the map, not the actual connection
351 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
353 wxNode
*node
= m_connections
.First();
355 while (node
&& !found
)
357 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
358 if (connection
->m_hConv
== conv
)
363 else node
= node
->Next();
372 wxDDEConnection::wxDDEConnection(char *buffer
, int size
)
376 if (DDEDefaultIPCBuffer
== NULL
)
377 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
378 m_bufPtr
= DDEDefaultIPCBuffer
;
379 m_bufSize
= DDEDefaultIPCBufferSize
;
393 m_sendingData
= NULL
;
396 wxDDEConnection::wxDDEConnection(void)
399 m_sendingData
= NULL
;
402 if (DDEDefaultIPCBuffer
== NULL
)
403 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
405 m_bufPtr
= DDEDefaultIPCBuffer
;
406 m_bufSize
= DDEDefaultIPCBufferSize
;
410 wxDDEConnection::~wxDDEConnection(void)
413 m_server
->GetConnections().DeleteObject(this);
415 m_client
->GetConnections().DeleteObject(this);
418 // Calls that CLIENT can make
419 bool wxDDEConnection::Disconnect(void)
421 DDEDeleteConnection((HCONV
) m_hConv
);
422 return (DdeDisconnect((HCONV
) m_hConv
) != 0);
425 bool wxDDEConnection::Execute(char *data
, int size
, wxIPCFormat format
)
433 return (DdeClientTransaction((LPBYTE
)data
, size
, (HCONV
) m_hConv
,
434 NULL
, format
, XTYP_EXECUTE
, 5000, &result
) ? TRUE
: FALSE
);
437 char *wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
440 HSZ atom
= DDEGetAtom(item
);
442 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
443 atom
, format
, XTYP_REQUEST
, 5000, &result
);
445 DWORD len
= DdeGetData(returned_data
, (LPBYTE
)(m_bufPtr
), m_bufSize
, 0);
447 DdeFreeDataHandle(returned_data
);
449 if (size
) *size
= (int)len
;
457 bool wxDDEConnection::Poke(const wxString
& item
, char *data
, int size
, wxIPCFormat format
)
465 HSZ item_atom
= DDEGetAtom(item
);
466 return (DdeClientTransaction((LPBYTE
)data
, size
, (HCONV
) m_hConv
,
467 item_atom
, format
, XTYP_POKE
, 5000, &result
) ? TRUE
: FALSE
);
470 bool wxDDEConnection::StartAdvise(const wxString
& item
)
473 HSZ atom
= DDEGetAtom(item
);
475 return (DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
476 atom
, CF_TEXT
, XTYP_ADVSTART
, 5000, &result
) ? TRUE
: FALSE
);
479 bool wxDDEConnection::StopAdvise(const wxString
& item
)
482 HSZ atom
= DDEGetAtom(item
);
484 return (DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
485 atom
, CF_TEXT
, XTYP_ADVSTOP
, 5000, &result
) ? TRUE
: FALSE
);
488 // Calls that SERVER can make
489 bool wxDDEConnection::Advise(const wxString
& item
, char *data
, int size
, wxIPCFormat format
)
496 HSZ item_atom
= DDEGetAtom(item
);
497 HSZ topic_atom
= DDEGetAtom(m_topicName
);
498 m_sendingData
= data
;
501 return (DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0);
504 bool wxDDEConnection::OnDisconnect(void)
511 #define DDERETURN HDDEDATA
513 HDDEDATA EXPENTRY _EXPORT
_DDECallback(
528 char server_buf
[100];
529 DdeQueryString(DDEIdInst
, hsz1
, (LPSTR
)topic_buf
, sizeof(topic_buf
),
531 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)server_buf
, sizeof(topic_buf
),
533 wxDDEServer
*server
= DDEFindServer(server_buf
);
536 wxDDEConnection
*connection
=
537 (wxDDEConnection
*) server
->OnAcceptConnection(wxString(topic_buf
));
540 connection
->m_server
= server
;
541 server
->GetConnections().Append(connection
);
542 connection
->m_hConv
= 0;
543 connection
->m_topicName
= topic_buf
;
544 DDECurrentlyConnecting
= connection
;
545 return (DDERETURN
)TRUE
;
548 else return (DDERETURN
)0;
552 case XTYP_CONNECT_CONFIRM
:
554 if (DDECurrentlyConnecting
)
556 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
557 DDECurrentlyConnecting
= NULL
;
558 return (DDERETURN
)TRUE
;
564 case XTYP_DISCONNECT
:
566 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
567 if (connection
&& connection
->OnDisconnect())
569 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
570 return (DDERETURN
)TRUE
;
572 else return (DDERETURN
)0;
578 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
582 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
583 DdeFreeDataHandle(hData
);
584 if (connection
->OnExecute(connection
->m_topicName
, connection
->m_bufPtr
, (int)len
, (wxIPCFormat
) wFmt
))
585 return (DDERETURN
)DDE_FACK
;
587 return (DDERETURN
)DDE_FNOTPROCESSED
;
588 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
594 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
599 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
603 char *data
= connection
->OnRequest(connection
->m_topicName
, wxString(item_name
), &user_size
, (wxIPCFormat
) wFmt
);
606 if (user_size
< 0) user_size
= strlen(data
);
608 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
609 (LPBYTE
)data
, user_size
+ 1, 0, hsz2
, wFmt
, 0);
610 return (DDERETURN
)handle
;
611 } else return (DDERETURN
)0;
612 } else return (DDERETURN
)0;
618 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
623 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
625 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
626 DdeFreeDataHandle(hData
);
627 connection
->OnPoke(connection
->m_topicName
, wxString(item_name
), connection
->m_bufPtr
, (int)len
, (wxIPCFormat
) wFmt
);
628 return (DDERETURN
)DDE_FACK
;
629 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
635 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
640 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
643 return (DDERETURN
)connection
->OnStartAdvise(connection
->m_topicName
, wxString(item_name
));
644 } else return (DDERETURN
)0;
650 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
655 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
657 return (DDERETURN
)connection
->OnStopAdvise(connection
->m_topicName
, wxString(item_name
));
658 } else return (DDERETURN
)0;
664 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
666 if (connection
&& connection
->m_sendingData
)
668 HDDEDATA data
= DdeCreateDataHandle(DDEIdInst
,
669 (LPBYTE
)connection
->m_sendingData
,
670 connection
->m_dataSize
, 0, hsz2
, connection
->m_dataType
, 0);
671 connection
->m_sendingData
= NULL
;
672 return (DDERETURN
)data
;
673 } else return (DDERETURN
)NULL
;
679 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
684 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
687 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
688 DdeFreeDataHandle(hData
);
689 if (connection
->OnAdvise(connection
->m_topicName
, wxString(item_name
), connection
->m_bufPtr
, (int)len
, (wxIPCFormat
) wFmt
))
690 return (DDERETURN
)DDE_FACK
;
692 return (DDERETURN
)DDE_FNOTPROCESSED
;
693 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
701 static HSZ
DDEAddAtom(const wxString
& string
)
703 HSZ atom
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)string
, CP_WINANSI
);
704 wxAtomTable
.Append(string
, (wxObject
*)atom
);
708 static HSZ
DDEGetAtom(const wxString
& string
)
710 wxNode
*node
= wxAtomTable
.Find(string
);
712 return (HSZ
)node
->Data();
716 return (HSZ
)(wxAtomTable
.Find(string
)->Data());
720 void DDEPrintError(void)
723 switch (DdeGetLastError(DDEIdInst
))
725 case DMLERR_ADVACKTIMEOUT
:
726 err
= "A request for a synchronous advise transaction has timed out.";
729 err
= "The response to the transaction caused the DDE_FBUSY bit to be set.";
731 case DMLERR_DATAACKTIMEOUT
:
732 err
= "A request for a synchronous data transaction has timed out.";
734 case DMLERR_DLL_NOT_INITIALIZED
:
735 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.";
737 case DMLERR_DLL_USAGE
:
738 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.";
740 case DMLERR_EXECACKTIMEOUT
:
741 err
= "A request for a synchronous execute transaction has timed out.";
743 case DMLERR_INVALIDPARAMETER
:
744 err
= "A parameter failed to be validated by the DDEML.";
746 case DMLERR_LOW_MEMORY
:
747 err
= "A DDEML application has created a prolonged race condition.";
749 case DMLERR_MEMORY_ERROR
:
750 err
= "A memory allocation failed.";
752 case DMLERR_NO_CONV_ESTABLISHED
:
753 err
= "A client's attempt to establish a conversation has failed.";
755 case DMLERR_NOTPROCESSED
:
756 err
= "A transaction failed.";
758 case DMLERR_POKEACKTIMEOUT
:
759 err
= "A request for a synchronous poke transaction has timed out.";
761 case DMLERR_POSTMSG_FAILED
:
762 err
= "An internal call to the PostMessage function has failed. ";
764 case DMLERR_REENTRANCY
:
765 err
= "Reentrancy problem.";
767 case DMLERR_SERVER_DIED
:
768 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.";
770 case DMLERR_SYS_ERROR
:
771 err
= "An internal error has occurred in the DDEML.";
773 case DMLERR_UNADVACKTIMEOUT
:
774 err
= "A request to end an advise transaction has timed out.";
776 case DMLERR_UNFOUND_QUEUE_ID
:
777 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.";
780 err
= "Unrecognised error type.";
783 MessageBox((HWND
) NULL
, (LPCSTR
)err
, "DDE Error", MB_OK
| MB_ICONINFORMATION
);