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"
38 #include "wx/msw/gnuwin32/extra.h"
48 #define _EXPORT _export
51 #if !USE_SHARED_LIBRARY
52 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
53 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
54 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
57 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
58 static void DDEDeleteConnection(HCONV hConv
);
59 static wxDDEServer
*DDEFindServer(const wxString
& s
);
61 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(
71 // Add topic name to atom table before using in conversations
72 static HSZ
DDEAddAtom(const wxString
& string
);
73 static HSZ
DDEGetAtom(const wxString
& string
);
74 static void DDEPrintError(void);
76 static DWORD DDEIdInst
= 0L;
77 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
79 static wxList
wxAtomTable(wxKEY_STRING
);
80 static wxList wxDDEClientObjects
;
81 static wxList wxDDEServerObjects
;
83 char *DDEDefaultIPCBuffer
= NULL
;
84 int DDEDefaultIPCBufferSize
= 0;
91 static bool DDEInitialized
= FALSE
;
93 void wxDDEInitialize()
97 DDEInitialized
= TRUE
;
99 // Should insert filter flags
100 DdeInitialize(&DDEIdInst
, (PFNCALLBACK
)MakeProcInstance(
101 (FARPROC
)_DDECallback
, wxGetInstance()),
114 DdeUninitialize(DDEIdInst
);
117 if (DDEDefaultIPCBuffer
)
118 delete [] DDEDefaultIPCBuffer
;
121 // Global find connection
122 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
124 wxNode
*node
= wxDDEServerObjects
.First();
125 wxDDEConnection
*found
= NULL
;
126 while (node
&& !found
)
128 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
129 found
= object
->FindConnection((WXHCONV
) hConv
);
135 node
= wxDDEClientObjects
.First();
136 while (node
&& !found
)
138 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
139 found
= object
->FindConnection((WXHCONV
) hConv
);
145 // Global delete connection
146 static void DDEDeleteConnection(HCONV hConv
)
148 wxNode
*node
= wxDDEServerObjects
.First();
150 while (node
&& !found
)
152 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
153 found
= object
->DeleteConnection((WXHCONV
) hConv
);
159 node
= wxDDEServerObjects
.First();
160 while (node
&& !found
)
162 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
163 found
= object
->DeleteConnection((WXHCONV
) hConv
);
168 // Find a server from a service name
169 static wxDDEServer
*DDEFindServer(const wxString
& s
)
171 wxNode
*node
= wxDDEServerObjects
.First();
172 wxDDEServer
*found
= NULL
;
173 while (node
&& !found
)
175 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
177 if (object
->GetServiceName() == s
)
179 else node
= node
->Next();
189 wxDDEServer::wxDDEServer(void)
192 wxDDEServerObjects
.Append(this);
195 bool wxDDEServer::Create(const wxString
& server_name
)
197 m_serviceName
= server_name
;
198 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)server_name
, CP_WINANSI
);
200 if (DdeNameService(DDEIdInst
, serviceName
, NULL
, DNS_REGISTER
) == 0)
208 wxDDEServer::~wxDDEServer(void)
210 if (m_serviceName
!= "")
212 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)m_serviceName
, CP_WINANSI
);
213 if (DdeNameService(DDEIdInst
, serviceName
, NULL
, DNS_UNREGISTER
) == 0)
218 wxDDEServerObjects
.DeleteObject(this);
220 wxNode
*node
= m_connections
.First();
223 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
224 wxNode
*next
= node
->Next();
225 connection
->OnDisconnect(); // May delete the node implicitly
229 // If any left after this, delete them
230 node
= m_connections
.First();
233 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
234 wxNode
*next
= node
->Next();
240 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
242 return new wxDDEConnection
;
245 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
247 wxNode
*node
= m_connections
.First();
248 wxDDEConnection
*found
= NULL
;
249 while (node
&& !found
)
251 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
252 if (connection
->m_hConv
== conv
)
254 else node
= node
->Next();
259 // Only delete the entry in the map, not the actual connection
260 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
262 wxNode
*node
= m_connections
.First();
264 while (node
&& !found
)
266 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
267 if (connection
->m_hConv
== conv
)
272 else node
= node
->Next();
284 wxDDEClient::wxDDEClient(void)
286 wxDDEClientObjects
.Append(this);
289 wxDDEClient::~wxDDEClient(void)
291 wxDDEClientObjects
.DeleteObject(this);
292 wxNode
*node
= m_connections
.First();
295 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
296 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
297 node
= m_connections
.First();
301 bool wxDDEClient::ValidHost(const wxString
& /* host */)
306 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& /* host */, const wxString
& server_name
, const wxString
& topic
)
308 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)server_name
, CP_WINANSI
);
309 HSZ topic_atom
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)topic
, CP_WINANSI
);
311 HCONV hConv
= DdeConnect(DDEIdInst
, serviceName
, topic_atom
, (PCONVCONTEXT
)NULL
);
316 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
319 connection
->m_hConv
= (WXHCONV
) hConv
;
320 connection
->m_topicName
= topic
;
321 connection
->m_client
= this;
322 m_connections
.Append(connection
);
329 wxConnectionBase
*wxDDEClient::OnMakeConnection(void)
331 return new wxDDEConnection
;
334 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
336 wxNode
*node
= m_connections
.First();
337 wxDDEConnection
*found
= NULL
;
338 while (node
&& !found
)
340 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
341 if (connection
->m_hConv
== conv
)
343 else node
= node
->Next();
348 // Only delete the entry in the map, not the actual connection
349 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
351 wxNode
*node
= m_connections
.First();
353 while (node
&& !found
)
355 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
356 if (connection
->m_hConv
== conv
)
361 else node
= node
->Next();
370 wxDDEConnection::wxDDEConnection(char *buffer
, int size
)
374 if (DDEDefaultIPCBuffer
== NULL
)
375 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
376 m_bufPtr
= DDEDefaultIPCBuffer
;
377 m_bufSize
= DDEDefaultIPCBufferSize
;
391 m_sendingData
= NULL
;
394 wxDDEConnection::wxDDEConnection(void)
397 m_sendingData
= NULL
;
400 if (DDEDefaultIPCBuffer
== NULL
)
401 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
403 m_bufPtr
= DDEDefaultIPCBuffer
;
404 m_bufSize
= DDEDefaultIPCBufferSize
;
408 wxDDEConnection::~wxDDEConnection(void)
411 m_server
->GetConnections().DeleteObject(this);
413 m_client
->GetConnections().DeleteObject(this);
416 // Calls that CLIENT can make
417 bool wxDDEConnection::Disconnect(void)
419 DDEDeleteConnection((HCONV
) m_hConv
);
420 return (DdeDisconnect((HCONV
) m_hConv
) != 0);
423 bool wxDDEConnection::Execute(char *data
, int size
, wxDataFormat format
)
431 return (DdeClientTransaction((LPBYTE
)data
, size
, (HCONV
) m_hConv
,
432 NULL
, format
, XTYP_EXECUTE
, 5000, &result
) ? TRUE
: FALSE
);
435 char *wxDDEConnection::Request(const wxString
& item
, int *size
, wxDataFormat format
)
438 HSZ atom
= DDEGetAtom(item
);
440 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
441 atom
, format
, XTYP_REQUEST
, 5000, &result
);
443 DWORD len
= DdeGetData(returned_data
, (LPBYTE
)(m_bufPtr
), m_bufSize
, 0);
445 DdeFreeDataHandle(returned_data
);
447 if (size
) *size
= (int)len
;
455 bool wxDDEConnection::Poke(const wxString
& item
, char *data
, int size
, wxDataFormat format
)
463 HSZ item_atom
= DDEGetAtom(item
);
464 return (DdeClientTransaction((LPBYTE
)data
, size
, (HCONV
) m_hConv
,
465 item_atom
, format
, XTYP_POKE
, 5000, &result
) ? TRUE
: FALSE
);
468 bool wxDDEConnection::StartAdvise(const wxString
& item
)
471 HSZ atom
= DDEGetAtom(item
);
473 return (DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
474 atom
, CF_TEXT
, XTYP_ADVSTART
, 5000, &result
) ? TRUE
: FALSE
);
477 bool wxDDEConnection::StopAdvise(const wxString
& item
)
480 HSZ atom
= DDEGetAtom(item
);
482 return (DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
483 atom
, CF_TEXT
, XTYP_ADVSTOP
, 5000, &result
) ? TRUE
: FALSE
);
486 // Calls that SERVER can make
487 bool wxDDEConnection::Advise(const wxString
& item
, char *data
, int size
, wxDataFormat format
)
494 HSZ item_atom
= DDEGetAtom(item
);
495 HSZ topic_atom
= DDEGetAtom(m_topicName
);
496 m_sendingData
= data
;
499 return (DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0);
502 bool wxDDEConnection::OnDisconnect(void)
509 #define DDERETURN HDDEDATA
511 HDDEDATA EXPENTRY _EXPORT
_DDECallback(
526 char server_buf
[100];
527 DdeQueryString(DDEIdInst
, hsz1
, (LPSTR
)topic_buf
, sizeof(topic_buf
),
529 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)server_buf
, sizeof(topic_buf
),
531 wxDDEServer
*server
= DDEFindServer(server_buf
);
534 wxDDEConnection
*connection
=
535 (wxDDEConnection
*) server
->OnAcceptConnection(wxString(topic_buf
));
538 connection
->m_server
= server
;
539 server
->GetConnections().Append(connection
);
540 connection
->m_hConv
= 0;
541 connection
->m_topicName
= topic_buf
;
542 DDECurrentlyConnecting
= connection
;
543 return (DDERETURN
)TRUE
;
546 else return (DDERETURN
)0;
550 case XTYP_CONNECT_CONFIRM
:
552 if (DDECurrentlyConnecting
)
554 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
555 DDECurrentlyConnecting
= NULL
;
556 return (DDERETURN
)TRUE
;
562 case XTYP_DISCONNECT
:
564 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
565 if (connection
&& connection
->OnDisconnect())
567 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
568 return (DDERETURN
)TRUE
;
570 else return (DDERETURN
)0;
576 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
580 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
581 DdeFreeDataHandle(hData
);
582 if (connection
->OnExecute(connection
->m_topicName
, connection
->m_bufPtr
, (int)len
, (wxDataFormat
) wFmt
))
583 return (DDERETURN
)DDE_FACK
;
585 return (DDERETURN
)DDE_FNOTPROCESSED
;
586 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
592 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
597 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
601 char *data
= connection
->OnRequest(connection
->m_topicName
, wxString(item_name
), &user_size
, (wxDataFormat
) wFmt
);
604 if (user_size
< 0) user_size
= strlen(data
);
606 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
607 (LPBYTE
)data
, user_size
+ 1, 0, hsz2
, wFmt
, 0);
608 return (DDERETURN
)handle
;
609 } else return (DDERETURN
)0;
610 } else return (DDERETURN
)0;
616 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
621 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
623 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
624 DdeFreeDataHandle(hData
);
625 connection
->OnPoke(connection
->m_topicName
, wxString(item_name
), connection
->m_bufPtr
, (int)len
, (wxDataFormat
) wFmt
);
626 return (DDERETURN
)DDE_FACK
;
627 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
633 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
638 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
641 return (DDERETURN
)connection
->OnStartAdvise(connection
->m_topicName
, wxString(item_name
));
642 } else return (DDERETURN
)0;
648 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
653 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
655 return (DDERETURN
)connection
->OnStopAdvise(connection
->m_topicName
, wxString(item_name
));
656 } else return (DDERETURN
)0;
662 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
664 if (connection
&& connection
->m_sendingData
)
666 HDDEDATA data
= DdeCreateDataHandle(DDEIdInst
,
667 (LPBYTE
)connection
->m_sendingData
,
668 connection
->m_dataSize
, 0, hsz2
, connection
->m_dataType
, 0);
669 connection
->m_sendingData
= NULL
;
670 return (DDERETURN
)data
;
671 } else return (DDERETURN
)NULL
;
677 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
682 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
685 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
686 DdeFreeDataHandle(hData
);
687 if (connection
->OnAdvise(connection
->m_topicName
, wxString(item_name
), connection
->m_bufPtr
, (int)len
, (wxDataFormat
) wFmt
))
688 return (DDERETURN
)DDE_FACK
;
690 return (DDERETURN
)DDE_FNOTPROCESSED
;
691 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
699 static HSZ
DDEAddAtom(const wxString
& string
)
701 HSZ atom
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)string
, CP_WINANSI
);
702 wxAtomTable
.Append(string
, (wxObject
*)atom
);
706 static HSZ
DDEGetAtom(const wxString
& string
)
708 wxNode
*node
= wxAtomTable
.Find(string
);
710 return (HSZ
)node
->Data();
714 return (HSZ
)(wxAtomTable
.Find(string
)->Data());
718 void DDEPrintError(void)
721 switch (DdeGetLastError(DDEIdInst
))
723 case DMLERR_ADVACKTIMEOUT
:
724 err
= "A request for a synchronous advise transaction has timed out.";
727 err
= "The response to the transaction caused the DDE_FBUSY bit to be set.";
729 case DMLERR_DATAACKTIMEOUT
:
730 err
= "A request for a synchronous data transaction has timed out.";
732 case DMLERR_DLL_NOT_INITIALIZED
:
733 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.";
735 case DMLERR_DLL_USAGE
:
736 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.";
738 case DMLERR_EXECACKTIMEOUT
:
739 err
= "A request for a synchronous execute transaction has timed out.";
741 case DMLERR_INVALIDPARAMETER
:
742 err
= "A parameter failed to be validated by the DDEML.";
744 case DMLERR_LOW_MEMORY
:
745 err
= "A DDEML application has created a prolonged race condition.";
747 case DMLERR_MEMORY_ERROR
:
748 err
= "A memory allocation failed.";
750 case DMLERR_NO_CONV_ESTABLISHED
:
751 err
= "A client's attempt to establish a conversation has failed.";
753 case DMLERR_NOTPROCESSED
:
754 err
= "A transaction failed.";
756 case DMLERR_POKEACKTIMEOUT
:
757 err
= "A request for a synchronous poke transaction has timed out.";
759 case DMLERR_POSTMSG_FAILED
:
760 err
= "An internal call to the PostMessage function has failed. ";
762 case DMLERR_REENTRANCY
:
763 err
= "Reentrancy problem.";
765 case DMLERR_SERVER_DIED
:
766 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.";
768 case DMLERR_SYS_ERROR
:
769 err
= "An internal error has occurred in the DDEML.";
771 case DMLERR_UNADVACKTIMEOUT
:
772 err
= "A request to end an advise transaction has timed out.";
774 case DMLERR_UNFOUND_QUEUE_ID
:
775 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.";
778 err
= "Unrecognised error type.";
781 MessageBox(NULL
, (LPCSTR
)err
, "DDE Error", MB_OK
| MB_ICONINFORMATION
);