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/module.h"
37 #include "wx/msw/private.h"
42 #if !wxUSE_NORLANDER_HEADERS
44 #include "wx/msw/gnuwin32/extra.h"
54 #define _EXPORT _export
57 #if !USE_SHARED_LIBRARY
58 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
59 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
60 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
63 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
64 static void DDEDeleteConnection(HCONV hConv
);
65 static wxDDEServer
*DDEFindServer(const wxString
& s
);
67 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(
77 // Add topic name to atom table before using in conversations
78 static HSZ
DDEAddAtom(const wxString
& string
);
79 static HSZ
DDEGetAtom(const wxString
& string
);
80 static void DDEPrintError(void);
82 static DWORD DDEIdInst
= 0L;
83 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
85 static wxList
wxAtomTable(wxKEY_STRING
);
86 static wxList wxDDEClientObjects
;
87 static wxList wxDDEServerObjects
;
89 char *DDEDefaultIPCBuffer
= NULL
;
90 int DDEDefaultIPCBufferSize
= 0;
97 static bool DDEInitialized
= FALSE
;
99 void wxDDEInitialize()
103 DDEInitialized
= TRUE
;
105 // Should insert filter flags
106 DdeInitialize(&DDEIdInst
, (PFNCALLBACK
)MakeProcInstance(
107 (FARPROC
)_DDECallback
, wxGetInstance()),
120 DdeUninitialize(DDEIdInst
);
123 if (DDEDefaultIPCBuffer
)
124 delete [] DDEDefaultIPCBuffer
;
127 // A module to allow DDE initialization/cleanup
128 // without calling these functions from app.cpp or from
129 // the user's application.
131 class wxDDEModule
: public wxModule
133 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
136 bool OnInit() { wxDDEInitialize(); return TRUE
; };
137 void OnExit() { wxDDECleanUp(); };
140 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
142 // Global find connection
143 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
145 wxNode
*node
= wxDDEServerObjects
.First();
146 wxDDEConnection
*found
= NULL
;
147 while (node
&& !found
)
149 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
150 found
= object
->FindConnection((WXHCONV
) hConv
);
156 node
= wxDDEClientObjects
.First();
157 while (node
&& !found
)
159 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
160 found
= object
->FindConnection((WXHCONV
) hConv
);
166 // Global delete connection
167 static void DDEDeleteConnection(HCONV hConv
)
169 wxNode
*node
= wxDDEServerObjects
.First();
171 while (node
&& !found
)
173 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
174 found
= object
->DeleteConnection((WXHCONV
) hConv
);
180 node
= wxDDEServerObjects
.First();
181 while (node
&& !found
)
183 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
184 found
= object
->DeleteConnection((WXHCONV
) hConv
);
189 // Find a server from a service name
190 static wxDDEServer
*DDEFindServer(const wxString
& s
)
192 wxNode
*node
= wxDDEServerObjects
.First();
193 wxDDEServer
*found
= NULL
;
194 while (node
&& !found
)
196 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
198 if (object
->GetServiceName() == s
)
200 else node
= node
->Next();
210 wxDDEServer::wxDDEServer(void)
213 wxDDEServerObjects
.Append(this);
216 bool wxDDEServer::Create(const wxString
& server_name
)
218 m_serviceName
= server_name
;
219 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, WXSTRINGCAST server_name
, CP_WINANSI
);
221 if (DdeNameService(DDEIdInst
, serviceName
, (HSZ
) NULL
, DNS_REGISTER
) == 0)
229 wxDDEServer::~wxDDEServer(void)
231 if (m_serviceName
!= T(""))
233 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, WXSTRINGCAST m_serviceName
, CP_WINANSI
);
234 if (DdeNameService(DDEIdInst
, serviceName
, NULL
, DNS_UNREGISTER
) == 0)
239 wxDDEServerObjects
.DeleteObject(this);
241 wxNode
*node
= m_connections
.First();
244 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
245 wxNode
*next
= node
->Next();
246 connection
->OnDisconnect(); // May delete the node implicitly
250 // If any left after this, delete them
251 node
= m_connections
.First();
254 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
255 wxNode
*next
= node
->Next();
261 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
263 return new wxDDEConnection
;
266 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
268 wxNode
*node
= m_connections
.First();
269 wxDDEConnection
*found
= NULL
;
270 while (node
&& !found
)
272 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
273 if (connection
->m_hConv
== conv
)
275 else node
= node
->Next();
280 // Only delete the entry in the map, not the actual connection
281 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
283 wxNode
*node
= m_connections
.First();
285 while (node
&& !found
)
287 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
288 if (connection
->m_hConv
== conv
)
293 else node
= node
->Next();
305 wxDDEClient::wxDDEClient(void)
307 wxDDEClientObjects
.Append(this);
310 wxDDEClient::~wxDDEClient(void)
312 wxDDEClientObjects
.DeleteObject(this);
313 wxNode
*node
= m_connections
.First();
316 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
317 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
318 node
= m_connections
.First();
322 bool wxDDEClient::ValidHost(const wxString
& /* host */)
327 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& /* host */, const wxString
& server_name
, const wxString
& topic
)
329 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, WXSTRINGCAST server_name
, CP_WINANSI
);
330 HSZ topic_atom
= DdeCreateStringHandle(DDEIdInst
, WXSTRINGCAST topic
, CP_WINANSI
);
332 HCONV hConv
= DdeConnect(DDEIdInst
, serviceName
, topic_atom
, (PCONVCONTEXT
)NULL
);
333 if (hConv
== (HCONV
) NULL
)
334 return (wxConnectionBase
*) NULL
;
337 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
340 connection
->m_hConv
= (WXHCONV
) hConv
;
341 connection
->m_topicName
= topic
;
342 connection
->m_client
= this;
343 m_connections
.Append(connection
);
346 else return (wxConnectionBase
*) NULL
;
350 wxConnectionBase
*wxDDEClient::OnMakeConnection(void)
352 return new wxDDEConnection
;
355 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
357 wxNode
*node
= m_connections
.First();
358 wxDDEConnection
*found
= NULL
;
359 while (node
&& !found
)
361 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
362 if (connection
->m_hConv
== conv
)
364 else node
= node
->Next();
369 // Only delete the entry in the map, not the actual connection
370 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
372 wxNode
*node
= m_connections
.First();
374 while (node
&& !found
)
376 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
377 if (connection
->m_hConv
== conv
)
382 else node
= node
->Next();
391 wxDDEConnection::wxDDEConnection(char *buffer
, int size
)
395 if (DDEDefaultIPCBuffer
== NULL
)
396 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
397 m_bufPtr
= DDEDefaultIPCBuffer
;
398 m_bufSize
= DDEDefaultIPCBufferSize
;
412 m_sendingData
= NULL
;
415 wxDDEConnection::wxDDEConnection(void)
418 m_sendingData
= NULL
;
421 if (DDEDefaultIPCBuffer
== NULL
)
422 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
424 m_bufPtr
= DDEDefaultIPCBuffer
;
425 m_bufSize
= DDEDefaultIPCBufferSize
;
429 wxDDEConnection::~wxDDEConnection(void)
432 m_server
->GetConnections().DeleteObject(this);
434 m_client
->GetConnections().DeleteObject(this);
437 // Calls that CLIENT can make
438 bool wxDDEConnection::Disconnect(void)
440 DDEDeleteConnection((HCONV
) m_hConv
);
441 return (DdeDisconnect((HCONV
) m_hConv
) != 0);
444 bool wxDDEConnection::Execute(const wxChar
*data
, int size
, wxIPCFormat format
)
448 size
= wxStrlen(data
);
452 return (DdeClientTransaction((LPBYTE
)data
, size
, (HCONV
) m_hConv
,
453 NULL
, format
, XTYP_EXECUTE
, 5000, &result
) ? TRUE
: FALSE
);
456 char *wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
459 HSZ atom
= DDEGetAtom(item
);
461 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
462 atom
, format
, XTYP_REQUEST
, 5000, &result
);
464 DWORD len
= DdeGetData(returned_data
, (LPBYTE
)(m_bufPtr
), m_bufSize
, 0);
466 DdeFreeDataHandle(returned_data
);
468 if (size
) *size
= (int)len
;
476 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
480 size
= wxStrlen(data
);
484 HSZ item_atom
= DDEGetAtom(item
);
485 return (DdeClientTransaction((LPBYTE
)data
, size
, (HCONV
) m_hConv
,
486 item_atom
, format
, XTYP_POKE
, 5000, &result
) ? TRUE
: FALSE
);
489 bool wxDDEConnection::StartAdvise(const wxString
& item
)
492 HSZ atom
= DDEGetAtom(item
);
494 return (DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
495 atom
, CF_TEXT
, XTYP_ADVSTART
, 5000, &result
) ? TRUE
: FALSE
);
498 bool wxDDEConnection::StopAdvise(const wxString
& item
)
501 HSZ atom
= DDEGetAtom(item
);
503 return (DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
504 atom
, CF_TEXT
, XTYP_ADVSTOP
, 5000, &result
) ? TRUE
: FALSE
);
507 // Calls that SERVER can make
508 bool wxDDEConnection::Advise(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
511 size
= wxStrlen(data
);
515 HSZ item_atom
= DDEGetAtom(item
);
516 HSZ topic_atom
= DDEGetAtom(m_topicName
);
517 m_sendingData
= data
;
520 return (DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0);
523 bool wxDDEConnection::OnDisconnect(void)
530 #define DDERETURN HDDEDATA
532 HDDEDATA EXPENTRY _EXPORT
_DDECallback(
546 wxChar topic_buf
[100];
547 wxChar server_buf
[100];
548 DdeQueryString(DDEIdInst
, hsz1
, (LPTSTR
)topic_buf
, WXSIZEOF(topic_buf
),
550 DdeQueryString(DDEIdInst
, hsz2
, (LPTSTR
)server_buf
, WXSIZEOF(topic_buf
),
552 wxDDEServer
*server
= DDEFindServer(server_buf
);
555 wxDDEConnection
*connection
=
556 (wxDDEConnection
*) server
->OnAcceptConnection(wxString(topic_buf
));
559 connection
->m_server
= server
;
560 server
->GetConnections().Append(connection
);
561 connection
->m_hConv
= 0;
562 connection
->m_topicName
= topic_buf
;
563 DDECurrentlyConnecting
= connection
;
564 return (DDERETURN
)(DWORD
)TRUE
;
567 else return (DDERETURN
)0;
571 case XTYP_CONNECT_CONFIRM
:
573 if (DDECurrentlyConnecting
)
575 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
576 DDECurrentlyConnecting
= NULL
;
577 return (DDERETURN
)(DWORD
)TRUE
;
583 case XTYP_DISCONNECT
:
585 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
586 if (connection
&& connection
->OnDisconnect())
588 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
589 return (DDERETURN
)(DWORD
)TRUE
;
591 else return (DDERETURN
)0;
597 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
601 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
602 DdeFreeDataHandle(hData
);
603 if (connection
->OnExecute(connection
->m_topicName
, connection
->m_bufPtr
, (int)len
, (wxIPCFormat
) wFmt
))
604 return (DDERETURN
)(DWORD
)DDE_FACK
;
606 return (DDERETURN
)DDE_FNOTPROCESSED
;
607 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
613 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
617 wxChar item_name
[200];
618 DdeQueryString(DDEIdInst
, hsz2
, (LPTSTR
)item_name
, WXSIZEOF(item_name
),
622 char *data
= connection
->OnRequest(connection
->m_topicName
, wxString(item_name
), &user_size
, (wxIPCFormat
) wFmt
);
625 if (user_size
< 0) user_size
= strlen(data
);
627 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
628 (LPBYTE
)data
, user_size
+ 1, 0, hsz2
, wFmt
, 0);
629 return (DDERETURN
)handle
;
630 } else return (DDERETURN
)0;
631 } else return (DDERETURN
)0;
637 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
641 wxChar item_name
[200];
642 DdeQueryString(DDEIdInst
, hsz2
, (LPTSTR
)item_name
, WXSIZEOF(item_name
),
644 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
645 DdeFreeDataHandle(hData
);
646 connection
->OnPoke(connection
->m_topicName
, wxString(item_name
), connection
->m_bufPtr
, (int)len
, (wxIPCFormat
) wFmt
);
647 return (DDERETURN
)(DWORD
)DDE_FACK
;
648 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
654 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
658 wxChar item_name
[200];
659 DdeQueryString(DDEIdInst
, hsz2
, (LPTSTR
)item_name
, WXSIZEOF(item_name
),
662 return (DDERETURN
)(DWORD
)connection
->OnStartAdvise(connection
->m_topicName
, wxString(item_name
));
663 } else return (DDERETURN
)0;
669 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
673 wxChar item_name
[200];
674 DdeQueryString(DDEIdInst
, hsz2
, (LPTSTR
)item_name
, WXSIZEOF(item_name
),
676 return (DDERETURN
)(DWORD
)connection
->OnStopAdvise(connection
->m_topicName
, wxString(item_name
));
677 } else return (DDERETURN
)0;
683 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
685 if (connection
&& connection
->m_sendingData
)
687 HDDEDATA data
= DdeCreateDataHandle(DDEIdInst
,
688 (LPBYTE
)connection
->m_sendingData
,
689 connection
->m_dataSize
, 0, hsz2
, connection
->m_dataType
, 0);
690 connection
->m_sendingData
= NULL
;
691 return (DDERETURN
)data
;
692 } else return (DDERETURN
)NULL
;
698 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
702 wxChar item_name
[200];
703 DdeQueryString(DDEIdInst
, hsz2
, (LPTSTR
)item_name
, WXSIZEOF(item_name
),
706 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
707 DdeFreeDataHandle(hData
);
708 if (connection
->OnAdvise(connection
->m_topicName
, wxString(item_name
), connection
->m_bufPtr
, (int)len
, (wxIPCFormat
) wFmt
))
709 return (DDERETURN
)(DWORD
)DDE_FACK
;
711 return (DDERETURN
)DDE_FNOTPROCESSED
;
712 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
720 static HSZ
DDEAddAtom(const wxString
& string
)
722 HSZ atom
= DdeCreateStringHandle(DDEIdInst
, WXSTRINGCAST string
, CP_WINANSI
);
723 wxAtomTable
.Append(string
, (wxObject
*)atom
);
727 static HSZ
DDEGetAtom(const wxString
& string
)
729 wxNode
*node
= wxAtomTable
.Find(string
);
731 return (HSZ
)node
->Data();
735 return (HSZ
)(wxAtomTable
.Find(string
)->Data());
739 void DDEPrintError(void)
742 switch (DdeGetLastError(DDEIdInst
))
744 case DMLERR_ADVACKTIMEOUT
:
745 err
= T("A request for a synchronous advise transaction has timed out.");
748 err
= T("The response to the transaction caused the DDE_FBUSY bit to be set.");
750 case DMLERR_DATAACKTIMEOUT
:
751 err
= T("A request for a synchronous data transaction has timed out.");
753 case DMLERR_DLL_NOT_INITIALIZED
:
754 err
= T("A DDEML function was called without first calling the DdeInitialize function,\n\ror an invalid instance identifier\n\rwas passed to a DDEML function.");
756 case DMLERR_DLL_USAGE
:
757 err
= T("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.");
759 case DMLERR_EXECACKTIMEOUT
:
760 err
= T("A request for a synchronous execute transaction has timed out.");
762 case DMLERR_INVALIDPARAMETER
:
763 err
= T("A parameter failed to be validated by the DDEML.");
765 case DMLERR_LOW_MEMORY
:
766 err
= T("A DDEML application has created a prolonged race condition.");
768 case DMLERR_MEMORY_ERROR
:
769 err
= T("A memory allocation failed.");
771 case DMLERR_NO_CONV_ESTABLISHED
:
772 err
= T("A client's attempt to establish a conversation has failed.");
774 case DMLERR_NOTPROCESSED
:
775 err
= T("A transaction failed.");
777 case DMLERR_POKEACKTIMEOUT
:
778 err
= T("A request for a synchronous poke transaction has timed out.");
780 case DMLERR_POSTMSG_FAILED
:
781 err
= T("An internal call to the PostMessage function has failed. ");
783 case DMLERR_REENTRANCY
:
784 err
= T("Reentrancy problem.");
786 case DMLERR_SERVER_DIED
:
787 err
= T("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.");
789 case DMLERR_SYS_ERROR
:
790 err
= T("An internal error has occurred in the DDEML.");
792 case DMLERR_UNADVACKTIMEOUT
:
793 err
= T("A request to end an advise transaction has timed out.");
795 case DMLERR_UNFOUND_QUEUE_ID
:
796 err
= T("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.");
799 err
= T("Unrecognised error type.");
802 MessageBox((HWND
) NULL
, (LPCTSTR
)err
, T("DDE Error"), MB_OK
| MB_ICONINFORMATION
);