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"
39 #include "wx/msw/gnuwin32/extra.h"
43 #include "wx/msw/private.h"
51 #define _EXPORT _export
54 #if !USE_SHARED_LIBRARY
55 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer
, wxServerBase
)
56 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient
, wxClientBase
)
57 IMPLEMENT_CLASS(wxDDEConnection
, wxConnectionBase
)
60 static wxDDEConnection
*DDEFindConnection(HCONV hConv
);
61 static void DDEDeleteConnection(HCONV hConv
);
62 static wxDDEServer
*DDEFindServer(const wxString
& s
);
64 extern "C" HDDEDATA EXPENTRY _EXPORT
_DDECallback(
74 // Add topic name to atom table before using in conversations
75 static HSZ
DDEAddAtom(const wxString
& string
);
76 static HSZ
DDEGetAtom(const wxString
& string
);
77 static void DDEPrintError(void);
79 static DWORD DDEIdInst
= 0L;
80 static wxDDEConnection
*DDECurrentlyConnecting
= NULL
;
82 static wxList
wxAtomTable(wxKEY_STRING
);
83 static wxList wxDDEClientObjects
;
84 static wxList wxDDEServerObjects
;
86 char *DDEDefaultIPCBuffer
= NULL
;
87 int DDEDefaultIPCBufferSize
= 0;
94 static bool DDEInitialized
= FALSE
;
96 void wxDDEInitialize()
100 DDEInitialized
= TRUE
;
102 // Should insert filter flags
103 DdeInitialize(&DDEIdInst
, (PFNCALLBACK
)MakeProcInstance(
104 (FARPROC
)_DDECallback
, wxGetInstance()),
117 DdeUninitialize(DDEIdInst
);
120 if (DDEDefaultIPCBuffer
)
121 delete [] DDEDefaultIPCBuffer
;
124 // A module to allow DDE initialization/cleanup
125 // without calling these functions from app.cpp or from
126 // the user's application.
128 class wxDDEModule
: public wxModule
130 DECLARE_DYNAMIC_CLASS(wxDDEModule
)
133 bool OnInit() { wxDDEInitialize(); return TRUE
; };
134 void OnExit() { wxDDECleanUp(); };
137 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule
, wxModule
)
139 // Global find connection
140 static wxDDEConnection
*DDEFindConnection(HCONV hConv
)
142 wxNode
*node
= wxDDEServerObjects
.First();
143 wxDDEConnection
*found
= NULL
;
144 while (node
&& !found
)
146 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
147 found
= object
->FindConnection((WXHCONV
) hConv
);
153 node
= wxDDEClientObjects
.First();
154 while (node
&& !found
)
156 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
157 found
= object
->FindConnection((WXHCONV
) hConv
);
163 // Global delete connection
164 static void DDEDeleteConnection(HCONV hConv
)
166 wxNode
*node
= wxDDEServerObjects
.First();
168 while (node
&& !found
)
170 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
171 found
= object
->DeleteConnection((WXHCONV
) hConv
);
177 node
= wxDDEServerObjects
.First();
178 while (node
&& !found
)
180 wxDDEClient
*object
= (wxDDEClient
*)node
->Data();
181 found
= object
->DeleteConnection((WXHCONV
) hConv
);
186 // Find a server from a service name
187 static wxDDEServer
*DDEFindServer(const wxString
& s
)
189 wxNode
*node
= wxDDEServerObjects
.First();
190 wxDDEServer
*found
= NULL
;
191 while (node
&& !found
)
193 wxDDEServer
*object
= (wxDDEServer
*)node
->Data();
195 if (object
->GetServiceName() == s
)
197 else node
= node
->Next();
207 wxDDEServer::wxDDEServer(void)
210 wxDDEServerObjects
.Append(this);
213 bool wxDDEServer::Create(const wxString
& server_name
)
215 m_serviceName
= server_name
;
216 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)server_name
, CP_WINANSI
);
218 if (DdeNameService(DDEIdInst
, serviceName
, (HSZ
) NULL
, DNS_REGISTER
) == 0)
226 wxDDEServer::~wxDDEServer(void)
228 if (m_serviceName
!= "")
230 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)m_serviceName
, CP_WINANSI
);
231 if (DdeNameService(DDEIdInst
, serviceName
, NULL
, DNS_UNREGISTER
) == 0)
236 wxDDEServerObjects
.DeleteObject(this);
238 wxNode
*node
= m_connections
.First();
241 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
242 wxNode
*next
= node
->Next();
243 connection
->OnDisconnect(); // May delete the node implicitly
247 // If any left after this, delete them
248 node
= m_connections
.First();
251 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
252 wxNode
*next
= node
->Next();
258 wxConnectionBase
*wxDDEServer::OnAcceptConnection(const wxString
& /* topic */)
260 return new wxDDEConnection
;
263 wxDDEConnection
*wxDDEServer::FindConnection(WXHCONV conv
)
265 wxNode
*node
= m_connections
.First();
266 wxDDEConnection
*found
= NULL
;
267 while (node
&& !found
)
269 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
270 if (connection
->m_hConv
== conv
)
272 else node
= node
->Next();
277 // Only delete the entry in the map, not the actual connection
278 bool wxDDEServer::DeleteConnection(WXHCONV conv
)
280 wxNode
*node
= m_connections
.First();
282 while (node
&& !found
)
284 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
285 if (connection
->m_hConv
== conv
)
290 else node
= node
->Next();
302 wxDDEClient::wxDDEClient(void)
304 wxDDEClientObjects
.Append(this);
307 wxDDEClient::~wxDDEClient(void)
309 wxDDEClientObjects
.DeleteObject(this);
310 wxNode
*node
= m_connections
.First();
313 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
314 delete connection
; // Deletes the node implicitly (see ~wxDDEConnection)
315 node
= m_connections
.First();
319 bool wxDDEClient::ValidHost(const wxString
& /* host */)
324 wxConnectionBase
*wxDDEClient::MakeConnection(const wxString
& /* host */, const wxString
& server_name
, const wxString
& topic
)
326 HSZ serviceName
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)server_name
, CP_WINANSI
);
327 HSZ topic_atom
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)topic
, CP_WINANSI
);
329 HCONV hConv
= DdeConnect(DDEIdInst
, serviceName
, topic_atom
, (PCONVCONTEXT
)NULL
);
330 if (hConv
== (HCONV
) NULL
)
331 return (wxConnectionBase
*) NULL
;
334 wxDDEConnection
*connection
= (wxDDEConnection
*) OnMakeConnection();
337 connection
->m_hConv
= (WXHCONV
) hConv
;
338 connection
->m_topicName
= topic
;
339 connection
->m_client
= this;
340 m_connections
.Append(connection
);
343 else return (wxConnectionBase
*) NULL
;
347 wxConnectionBase
*wxDDEClient::OnMakeConnection(void)
349 return new wxDDEConnection
;
352 wxDDEConnection
*wxDDEClient::FindConnection(WXHCONV conv
)
354 wxNode
*node
= m_connections
.First();
355 wxDDEConnection
*found
= NULL
;
356 while (node
&& !found
)
358 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
359 if (connection
->m_hConv
== conv
)
361 else node
= node
->Next();
366 // Only delete the entry in the map, not the actual connection
367 bool wxDDEClient::DeleteConnection(WXHCONV conv
)
369 wxNode
*node
= m_connections
.First();
371 while (node
&& !found
)
373 wxDDEConnection
*connection
= (wxDDEConnection
*)node
->Data();
374 if (connection
->m_hConv
== conv
)
379 else node
= node
->Next();
388 wxDDEConnection::wxDDEConnection(char *buffer
, int size
)
392 if (DDEDefaultIPCBuffer
== NULL
)
393 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
394 m_bufPtr
= DDEDefaultIPCBuffer
;
395 m_bufSize
= DDEDefaultIPCBufferSize
;
409 m_sendingData
= NULL
;
412 wxDDEConnection::wxDDEConnection(void)
415 m_sendingData
= NULL
;
418 if (DDEDefaultIPCBuffer
== NULL
)
419 DDEDefaultIPCBuffer
= new char[DDEDefaultIPCBufferSize
];
421 m_bufPtr
= DDEDefaultIPCBuffer
;
422 m_bufSize
= DDEDefaultIPCBufferSize
;
426 wxDDEConnection::~wxDDEConnection(void)
429 m_server
->GetConnections().DeleteObject(this);
431 m_client
->GetConnections().DeleteObject(this);
434 // Calls that CLIENT can make
435 bool wxDDEConnection::Disconnect(void)
437 DDEDeleteConnection((HCONV
) m_hConv
);
438 return (DdeDisconnect((HCONV
) m_hConv
) != 0);
441 bool wxDDEConnection::Execute(char *data
, int size
, wxIPCFormat format
)
449 return (DdeClientTransaction((LPBYTE
)data
, size
, (HCONV
) m_hConv
,
450 NULL
, format
, XTYP_EXECUTE
, 5000, &result
) ? TRUE
: FALSE
);
453 char *wxDDEConnection::Request(const wxString
& item
, int *size
, wxIPCFormat format
)
456 HSZ atom
= DDEGetAtom(item
);
458 HDDEDATA returned_data
= DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
459 atom
, format
, XTYP_REQUEST
, 5000, &result
);
461 DWORD len
= DdeGetData(returned_data
, (LPBYTE
)(m_bufPtr
), m_bufSize
, 0);
463 DdeFreeDataHandle(returned_data
);
465 if (size
) *size
= (int)len
;
473 bool wxDDEConnection::Poke(const wxString
& item
, char *data
, int size
, wxIPCFormat format
)
481 HSZ item_atom
= DDEGetAtom(item
);
482 return (DdeClientTransaction((LPBYTE
)data
, size
, (HCONV
) m_hConv
,
483 item_atom
, format
, XTYP_POKE
, 5000, &result
) ? TRUE
: FALSE
);
486 bool wxDDEConnection::StartAdvise(const wxString
& item
)
489 HSZ atom
= DDEGetAtom(item
);
491 return (DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
492 atom
, CF_TEXT
, XTYP_ADVSTART
, 5000, &result
) ? TRUE
: FALSE
);
495 bool wxDDEConnection::StopAdvise(const wxString
& item
)
498 HSZ atom
= DDEGetAtom(item
);
500 return (DdeClientTransaction(NULL
, 0, (HCONV
) m_hConv
,
501 atom
, CF_TEXT
, XTYP_ADVSTOP
, 5000, &result
) ? TRUE
: FALSE
);
504 // Calls that SERVER can make
505 bool wxDDEConnection::Advise(const wxString
& item
, char *data
, int size
, wxIPCFormat format
)
512 HSZ item_atom
= DDEGetAtom(item
);
513 HSZ topic_atom
= DDEGetAtom(m_topicName
);
514 m_sendingData
= data
;
517 return (DdePostAdvise(DDEIdInst
, topic_atom
, item_atom
) != 0);
520 bool wxDDEConnection::OnDisconnect(void)
527 #define DDERETURN HDDEDATA
529 HDDEDATA EXPENTRY _EXPORT
_DDECallback(
544 char server_buf
[100];
545 DdeQueryString(DDEIdInst
, hsz1
, (LPSTR
)topic_buf
, sizeof(topic_buf
),
547 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)server_buf
, sizeof(topic_buf
),
549 wxDDEServer
*server
= DDEFindServer(server_buf
);
552 wxDDEConnection
*connection
=
553 (wxDDEConnection
*) server
->OnAcceptConnection(wxString(topic_buf
));
556 connection
->m_server
= server
;
557 server
->GetConnections().Append(connection
);
558 connection
->m_hConv
= 0;
559 connection
->m_topicName
= topic_buf
;
560 DDECurrentlyConnecting
= connection
;
561 return (DDERETURN
)TRUE
;
564 else return (DDERETURN
)0;
568 case XTYP_CONNECT_CONFIRM
:
570 if (DDECurrentlyConnecting
)
572 DDECurrentlyConnecting
->m_hConv
= (WXHCONV
) hConv
;
573 DDECurrentlyConnecting
= NULL
;
574 return (DDERETURN
)TRUE
;
580 case XTYP_DISCONNECT
:
582 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
583 if (connection
&& connection
->OnDisconnect())
585 DDEDeleteConnection(hConv
); // Delete mapping: hConv => connection
586 return (DDERETURN
)TRUE
;
588 else return (DDERETURN
)0;
594 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
598 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
599 DdeFreeDataHandle(hData
);
600 if (connection
->OnExecute(connection
->m_topicName
, connection
->m_bufPtr
, (int)len
, (wxIPCFormat
) wFmt
))
601 return (DDERETURN
)DDE_FACK
;
603 return (DDERETURN
)DDE_FNOTPROCESSED
;
604 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
610 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
615 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
619 char *data
= connection
->OnRequest(connection
->m_topicName
, wxString(item_name
), &user_size
, (wxIPCFormat
) wFmt
);
622 if (user_size
< 0) user_size
= strlen(data
);
624 HDDEDATA handle
= DdeCreateDataHandle(DDEIdInst
,
625 (LPBYTE
)data
, user_size
+ 1, 0, hsz2
, wFmt
, 0);
626 return (DDERETURN
)handle
;
627 } else return (DDERETURN
)0;
628 } else return (DDERETURN
)0;
634 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
639 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
641 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
642 DdeFreeDataHandle(hData
);
643 connection
->OnPoke(connection
->m_topicName
, wxString(item_name
), connection
->m_bufPtr
, (int)len
, (wxIPCFormat
) wFmt
);
644 return (DDERETURN
)DDE_FACK
;
645 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
651 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
656 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
659 return (DDERETURN
)connection
->OnStartAdvise(connection
->m_topicName
, wxString(item_name
));
660 } else return (DDERETURN
)0;
666 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
671 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
673 return (DDERETURN
)connection
->OnStopAdvise(connection
->m_topicName
, wxString(item_name
));
674 } else return (DDERETURN
)0;
680 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
682 if (connection
&& connection
->m_sendingData
)
684 HDDEDATA data
= DdeCreateDataHandle(DDEIdInst
,
685 (LPBYTE
)connection
->m_sendingData
,
686 connection
->m_dataSize
, 0, hsz2
, connection
->m_dataType
, 0);
687 connection
->m_sendingData
= NULL
;
688 return (DDERETURN
)data
;
689 } else return (DDERETURN
)NULL
;
695 wxDDEConnection
*connection
= DDEFindConnection(hConv
);
700 DdeQueryString(DDEIdInst
, hsz2
, (LPSTR
)item_name
, sizeof(item_name
),
703 DWORD len
= DdeGetData(hData
, (LPBYTE
)(connection
->m_bufPtr
), connection
->m_bufSize
, 0);
704 DdeFreeDataHandle(hData
);
705 if (connection
->OnAdvise(connection
->m_topicName
, wxString(item_name
), connection
->m_bufPtr
, (int)len
, (wxIPCFormat
) wFmt
))
706 return (DDERETURN
)DDE_FACK
;
708 return (DDERETURN
)DDE_FNOTPROCESSED
;
709 } else return (DDERETURN
)DDE_FNOTPROCESSED
;
717 static HSZ
DDEAddAtom(const wxString
& string
)
719 HSZ atom
= DdeCreateStringHandle(DDEIdInst
, (char*) (const char *)string
, CP_WINANSI
);
720 wxAtomTable
.Append(string
, (wxObject
*)atom
);
724 static HSZ
DDEGetAtom(const wxString
& string
)
726 wxNode
*node
= wxAtomTable
.Find(string
);
728 return (HSZ
)node
->Data();
732 return (HSZ
)(wxAtomTable
.Find(string
)->Data());
736 void DDEPrintError(void)
739 switch (DdeGetLastError(DDEIdInst
))
741 case DMLERR_ADVACKTIMEOUT
:
742 err
= "A request for a synchronous advise transaction has timed out.";
745 err
= "The response to the transaction caused the DDE_FBUSY bit to be set.";
747 case DMLERR_DATAACKTIMEOUT
:
748 err
= "A request for a synchronous data transaction has timed out.";
750 case DMLERR_DLL_NOT_INITIALIZED
:
751 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.";
753 case DMLERR_DLL_USAGE
:
754 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.";
756 case DMLERR_EXECACKTIMEOUT
:
757 err
= "A request for a synchronous execute transaction has timed out.";
759 case DMLERR_INVALIDPARAMETER
:
760 err
= "A parameter failed to be validated by the DDEML.";
762 case DMLERR_LOW_MEMORY
:
763 err
= "A DDEML application has created a prolonged race condition.";
765 case DMLERR_MEMORY_ERROR
:
766 err
= "A memory allocation failed.";
768 case DMLERR_NO_CONV_ESTABLISHED
:
769 err
= "A client's attempt to establish a conversation has failed.";
771 case DMLERR_NOTPROCESSED
:
772 err
= "A transaction failed.";
774 case DMLERR_POKEACKTIMEOUT
:
775 err
= "A request for a synchronous poke transaction has timed out.";
777 case DMLERR_POSTMSG_FAILED
:
778 err
= "An internal call to the PostMessage function has failed. ";
780 case DMLERR_REENTRANCY
:
781 err
= "Reentrancy problem.";
783 case DMLERR_SERVER_DIED
:
784 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.";
786 case DMLERR_SYS_ERROR
:
787 err
= "An internal error has occurred in the DDEML.";
789 case DMLERR_UNADVACKTIMEOUT
:
790 err
= "A request to end an advise transaction has timed out.";
792 case DMLERR_UNFOUND_QUEUE_ID
:
793 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.";
796 err
= "Unrecognised error type.";
799 MessageBox((HWND
) NULL
, (LPCSTR
)err
, "DDE Error", MB_OK
| MB_ICONINFORMATION
);