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"
43 #include "wx/msw/gnuwin32/extra.h"
52 #define _EXPORT _export
55 #if !USE_SHARED_LIBRARY
56 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
57 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
58 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
61 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
62 static void DDEDeleteConnection(HCONV hConv
);
63 static wxDDEServer
*DDEFindServer(const wxString
& s
);
65 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(
75 // Add topic name to atom table before using in conversations
76 static HSZ
DDEAddAtom(const wxString
& string
);
77 static HSZ
DDEGetAtom(const wxString
& string
);
78 static void DDEPrintError(void);
80 static DWORD DDEIdInst
= 0L;
81 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
83 static wxList
wxAtomTable(wxKEY_STRING
);
84 static wxList wxDDEClientObjects
;
85 static wxList wxDDEServerObjects
;
87 char *DDEDefaultIPCBuffer
= NULL
;
88 int DDEDefaultIPCBufferSize
= 0;
95 static bool DDEInitialized
= FALSE
;
97 void wxDDEInitialize()
101 DDEInitialized
= TRUE
;
103 // Should insert filter flags
104 DdeInitialize(&DDEIdInst
, (PFNCALLBACK
)MakeProcInstance(
105 (FARPROC
)_DDECallback
, wxGetInstance()),
118 DdeUninitialize(DDEIdInst
);
121 if (DDEDefaultIPCBuffer
)
122 delete [] DDEDefaultIPCBuffer
;
125 // A module to allow DDE initialization/cleanup
126 // without calling these functions from app.cpp or from
127 // the user's application.
129 class wxDDEModule
: public wxModule
131 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
134 bool OnInit() { wxDDEInitialize(); return TRUE
; };
135 void OnExit() { wxDDECleanUp(); };
138 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
140 // Global find connection
141 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
143 wxNode
*node
= wxDDEServerObjects
.First();
144 wxDDEConnection
*found
= NULL
;
145 while (node
&& !found
)
147 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
148 found
= object
->FindConnection((WXHCONV
) hConv
);
154 node
= wxDDEClientObjects
.First();
155 while (node
&& !found
)
157 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
158 found
= object
->FindConnection((WXHCONV
) hConv
);
164 // Global delete connection
165 static void DDEDeleteConnection(HCONV hConv
)
167 wxNode
*node
= wxDDEServerObjects
.First();
169 while (node
&& !found
)
171 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
172 found
= object
->DeleteConnection((WXHCONV
) hConv
);
178 node
= wxDDEServerObjects
.First();
179 while (node
&& !found
)
181 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
182 found
= object
->DeleteConnection((WXHCONV
) hConv
);
187 // Find a server from a service name
188 static wxDDEServer
*DDEFindServer(const wxString
& s
)
190 wxNode
*node
= wxDDEServerObjects
.First();
191 wxDDEServer
*found
= NULL
;
192 while (node
&& !found
)
194 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
196 if (object
->GetServiceName() == s
)
198 else node
= node
->Next();
208 wxDDEServer::wxDDEServer(void)
211 wxDDEServerObjects
.Append(this);
214 bool wxDDEServer::Create(const wxString
& server_name
)
216 m_serviceName
= server_name
;
217 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, WXSTRINGCAST server_name
, CP_WINANSI
);
219 if (DdeNameService(DDEIdInst
, serviceName
, (HSZ
) NULL
, DNS_REGISTER
) == 0)
227 wxDDEServer::~wxDDEServer(void)
229 if (m_serviceName
!= _T(""))
231 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, WXSTRINGCAST m_serviceName
, CP_WINANSI
);
232 if (DdeNameService(DDEIdInst
, serviceName
, NULL
, DNS_UNREGISTER
) == 0)
237 wxDDEServerObjects
.DeleteObject(this);
239 wxNode
*node
= m_connections
.First();
242 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
243 wxNode
*next
= node
->Next();
244 connection
->OnDisconnect(); // May delete the node implicitly
248 // If any left after this, delete them
249 node
= m_connections
.First();
252 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
253 wxNode
*next
= node
->Next();
259 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
261 return new wxDDEConnection
;
264 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
266 wxNode
*node
= m_connections
.First();
267 wxDDEConnection
*found
= NULL
;
268 while (node
&& !found
)
270 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
271 if (connection
->m_hConv
== conv
)
273 else node
= node
->Next();
278 // Only delete the entry in the map, not the actual connection
279 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
281 wxNode
*node
= m_connections
.First();
283 while (node
&& !found
)
285 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
286 if (connection
->m_hConv
== conv
)
291 else node
= node
->Next();
303 wxDDEClient::wxDDEClient(void)
305 wxDDEClientObjects
.Append(this);
308 wxDDEClient::~wxDDEClient(void)
310 wxDDEClientObjects
.DeleteObject(this);
311 wxNode
*node
= m_connections
.First();
314 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
315 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
316 node
= m_connections
.First();
320 bool wxDDEClient::ValidHost(const wxString
& /* host */)
325 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& /* host */, const wxString
& server_name
, const wxString
& topic
)
327 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, WXSTRINGCAST server_name
, CP_WINANSI
);
328 HSZ topic_atom
= DdeCreateStringHandle(DDEIdInst
, WXSTRINGCAST topic
, CP_WINANSI
);
330 HCONV hConv
= DdeConnect(DDEIdInst
, serviceName
, topic_atom
, (PCONVCONTEXT
)NULL
);
331 if (hConv
== (HCONV
) NULL
)
332 return (wxConnectionBase
*) NULL
;
335 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
338 connection
->m_hConv
= (WXHCONV
) hConv
;
339 connection
->m_topicName
= topic
;
340 connection
->m_client
= this;
341 m_connections
.Append(connection
);
344 else return (wxConnectionBase
*) NULL
;
348 wxConnectionBase
*wxDDEClient::OnMakeConnection(void)
350 return new wxDDEConnection
;
353 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
355 wxNode
*node
= m_connections
.First();
356 wxDDEConnection
*found
= NULL
;
357 while (node
&& !found
)
359 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
360 if (connection
->m_hConv
== conv
)
362 else node
= node
->Next();
367 // Only delete the entry in the map, not the actual connection
368 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
370 wxNode
*node
= m_connections
.First();
372 while (node
&& !found
)
374 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
375 if (connection
->m_hConv
== conv
)
380 else node
= node
->Next();
389 wxDDEConnection::wxDDEConnection(char *buffer
, int size
)
393 if (DDEDefaultIPCBuffer
== NULL
)
394 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
395 m_bufPtr
= DDEDefaultIPCBuffer
;
396 m_bufSize
= DDEDefaultIPCBufferSize
;
410 m_sendingData
= NULL
;
413 wxDDEConnection::wxDDEConnection(void)
416 m_sendingData
= NULL
;
419 if (DDEDefaultIPCBuffer
== NULL
)
420 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
422 m_bufPtr
= DDEDefaultIPCBuffer
;
423 m_bufSize
= DDEDefaultIPCBufferSize
;
427 wxDDEConnection::~wxDDEConnection(void)
430 m_server
->GetConnections().DeleteObject(this);
432 m_client
->GetConnections().DeleteObject(this);
435 // Calls that CLIENT can make
436 bool wxDDEConnection::Disconnect(void)
438 DDEDeleteConnection((HCONV
) m_hConv
);
439 return (DdeDisconnect((HCONV
) m_hConv
) != 0);
442 bool wxDDEConnection::Execute(wxChar
*data
, int size
, wxIPCFormat format
)
446 size
= wxStrlen(data
);
450 return (DdeClientTransaction((LPBYTE
)data
, size
, (HCONV
) m_hConv
,
451 NULL
, format
, XTYP_EXECUTE
, 5000, &result
) ? TRUE
: FALSE
);
454 char *wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
457 HSZ atom
= DDEGetAtom(item
);
459 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
460 atom
, format
, XTYP_REQUEST
, 5000, &result
);
462 DWORD len
= DdeGetData(returned_data
, (LPBYTE
)(m_bufPtr
), m_bufSize
, 0);
464 DdeFreeDataHandle(returned_data
);
466 if (size
) *size
= (int)len
;
474 bool wxDDEConnection::Poke(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
478 size
= wxStrlen(data
);
482 HSZ item_atom
= DDEGetAtom(item
);
483 return (DdeClientTransaction((LPBYTE
)data
, size
, (HCONV
) m_hConv
,
484 item_atom
, format
, XTYP_POKE
, 5000, &result
) ? TRUE
: FALSE
);
487 bool wxDDEConnection::StartAdvise(const wxString
& item
)
490 HSZ atom
= DDEGetAtom(item
);
492 return (DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
493 atom
, CF_TEXT
, XTYP_ADVSTART
, 5000, &result
) ? TRUE
: FALSE
);
496 bool wxDDEConnection::StopAdvise(const wxString
& item
)
499 HSZ atom
= DDEGetAtom(item
);
501 return (DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
502 atom
, CF_TEXT
, XTYP_ADVSTOP
, 5000, &result
) ? TRUE
: FALSE
);
505 // Calls that SERVER can make
506 bool wxDDEConnection::Advise(const wxString
& item
, wxChar
*data
, int size
, wxIPCFormat format
)
509 size
= wxStrlen(data
);
513 HSZ item_atom
= DDEGetAtom(item
);
514 HSZ topic_atom
= DDEGetAtom(m_topicName
);
515 m_sendingData
= data
;
518 return (DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0);
521 bool wxDDEConnection::OnDisconnect(void)
528 #define DDERETURN HDDEDATA
530 HDDEDATA EXPENTRY _EXPORT
_DDECallback(
544 wxChar topic_buf
[100];
545 wxChar server_buf
[100];
546 DdeQueryString(DDEIdInst
, hsz1
, (LPTSTR
)topic_buf
, WXSIZEOF(topic_buf
),
548 DdeQueryString(DDEIdInst
, hsz2
, (LPTSTR
)server_buf
, WXSIZEOF(topic_buf
),
550 wxDDEServer
*server
= DDEFindServer(server_buf
);
553 wxDDEConnection
*connection
=
554 (wxDDEConnection
*) server
->OnAcceptConnection(wxString(topic_buf
));
557 connection
->m_server
= server
;
558 server
->GetConnections().Append(connection
);
559 connection
->m_hConv
= 0;
560 connection
->m_topicName
= topic_buf
;
561 DDECurrentlyConnecting
= connection
;
562 return (DDERETURN
)TRUE
;
565 else return (DDERETURN
)0;
569 case XTYP_CONNECT_CONFIRM
:
571 if (DDECurrentlyConnecting
)
573 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
574 DDECurrentlyConnecting
= NULL
;
575 return (DDERETURN
)TRUE
;
581 case XTYP_DISCONNECT
:
583 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
584 if (connection
&& connection
->OnDisconnect())
586 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
587 return (DDERETURN
)TRUE
;
589 else return (DDERETURN
)0;
595 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
599 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
600 DdeFreeDataHandle(hData
);
601 if (connection
->OnExecute(connection
->m_topicName
, connection
->m_bufPtr
, (int)len
, (wxIPCFormat
) wFmt
))
602 return (DDERETURN
)DDE_FACK
;
604 return (DDERETURN
)DDE_FNOTPROCESSED
;
605 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
611 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
615 wxChar item_name
[200];
616 DdeQueryString(DDEIdInst
, hsz2
, (LPTSTR
)item_name
, WXSIZEOF(item_name
),
620 char *data
= connection
->OnRequest(connection
->m_topicName
, wxString(item_name
), &user_size
, (wxIPCFormat
) wFmt
);
623 if (user_size
< 0) user_size
= strlen(data
);
625 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
626 (LPBYTE
)data
, user_size
+ 1, 0, hsz2
, wFmt
, 0);
627 return (DDERETURN
)handle
;
628 } else return (DDERETURN
)0;
629 } else return (DDERETURN
)0;
635 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
639 wxChar item_name
[200];
640 DdeQueryString(DDEIdInst
, hsz2
, (LPTSTR
)item_name
, WXSIZEOF(item_name
),
642 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
643 DdeFreeDataHandle(hData
);
644 connection
->OnPoke(connection
->m_topicName
, wxString(item_name
), connection
->m_bufPtr
, (int)len
, (wxIPCFormat
) wFmt
);
645 return (DDERETURN
)DDE_FACK
;
646 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
652 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
656 wxChar item_name
[200];
657 DdeQueryString(DDEIdInst
, hsz2
, (LPTSTR
)item_name
, WXSIZEOF(item_name
),
660 return (DDERETURN
)connection
->OnStartAdvise(connection
->m_topicName
, wxString(item_name
));
661 } else return (DDERETURN
)0;
667 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
671 wxChar item_name
[200];
672 DdeQueryString(DDEIdInst
, hsz2
, (LPTSTR
)item_name
, WXSIZEOF(item_name
),
674 return (DDERETURN
)connection
->OnStopAdvise(connection
->m_topicName
, wxString(item_name
));
675 } else return (DDERETURN
)0;
681 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
683 if (connection
&& connection
->m_sendingData
)
685 HDDEDATA data
= DdeCreateDataHandle(DDEIdInst
,
686 (LPBYTE
)connection
->m_sendingData
,
687 connection
->m_dataSize
, 0, hsz2
, connection
->m_dataType
, 0);
688 connection
->m_sendingData
= NULL
;
689 return (DDERETURN
)data
;
690 } else return (DDERETURN
)NULL
;
696 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
700 wxChar item_name
[200];
701 DdeQueryString(DDEIdInst
, hsz2
, (LPTSTR
)item_name
, WXSIZEOF(item_name
),
704 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
705 DdeFreeDataHandle(hData
);
706 if (connection
->OnAdvise(connection
->m_topicName
, wxString(item_name
), connection
->m_bufPtr
, (int)len
, (wxIPCFormat
) wFmt
))
707 return (DDERETURN
)DDE_FACK
;
709 return (DDERETURN
)DDE_FNOTPROCESSED
;
710 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
718 static HSZ
DDEAddAtom(const wxString
& string
)
720 HSZ atom
= DdeCreateStringHandle(DDEIdInst
, WXSTRINGCAST string
, CP_WINANSI
);
721 wxAtomTable
.Append(string
, (wxObject
*)atom
);
725 static HSZ
DDEGetAtom(const wxString
& string
)
727 wxNode
*node
= wxAtomTable
.Find(string
);
729 return (HSZ
)node
->Data();
733 return (HSZ
)(wxAtomTable
.Find(string
)->Data());
737 void DDEPrintError(void)
740 switch (DdeGetLastError(DDEIdInst
))
742 case DMLERR_ADVACKTIMEOUT
:
743 err
= _T("A request for a synchronous advise transaction has timed out.");
746 err
= _T("The response to the transaction caused the DDE_FBUSY bit to be set.");
748 case DMLERR_DATAACKTIMEOUT
:
749 err
= _T("A request for a synchronous data transaction has timed out.");
751 case DMLERR_DLL_NOT_INITIALIZED
:
752 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.");
754 case DMLERR_DLL_USAGE
:
755 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.");
757 case DMLERR_EXECACKTIMEOUT
:
758 err
= _T("A request for a synchronous execute transaction has timed out.");
760 case DMLERR_INVALIDPARAMETER
:
761 err
= _T("A parameter failed to be validated by the DDEML.");
763 case DMLERR_LOW_MEMORY
:
764 err
= _T("A DDEML application has created a prolonged race condition.");
766 case DMLERR_MEMORY_ERROR
:
767 err
= _T("A memory allocation failed.");
769 case DMLERR_NO_CONV_ESTABLISHED
:
770 err
= _T("A client's attempt to establish a conversation has failed.");
772 case DMLERR_NOTPROCESSED
:
773 err
= _T("A transaction failed.");
775 case DMLERR_POKEACKTIMEOUT
:
776 err
= _T("A request for a synchronous poke transaction has timed out.");
778 case DMLERR_POSTMSG_FAILED
:
779 err
= _T("An internal call to the PostMessage function has failed. ");
781 case DMLERR_REENTRANCY
:
782 err
= _T("Reentrancy problem.");
784 case DMLERR_SERVER_DIED
:
785 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.");
787 case DMLERR_SYS_ERROR
:
788 err
= _T("An internal error has occurred in the DDEML.");
790 case DMLERR_UNADVACKTIMEOUT
:
791 err
= _T("A request to end an advise transaction has timed out.");
793 case DMLERR_UNFOUND_QUEUE_ID
:
794 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.");
797 err
= _T("Unrecognised error type.");
800 MessageBox((HWND
) NULL
, (LPCTSTR
)err
, _T("DDE Error"), MB_OK
| MB_ICONINFORMATION
);