1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/dde.cpp
3 // Purpose:     DDE classes
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     01/02/97
7 // RCS-ID:      $Id: dde.cpp 40943 2006-08-31 19:31:43Z ABX $
8 // Copyright:   (c) Julian Smart
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 // ============================================================================
13 // declarations
14 // ============================================================================
15 
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19 
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22 
23 #ifdef __BORLANDC__
24     #pragma hdrstop
25 #endif
26 
27 #if wxUSE_IPC
28 
29 #ifndef WX_PRECOMP
30     #include "wx/utils.h"
31     #include "wx/app.h"
32     #include "wx/hashmap.h"
33     #include "wx/module.h"
34 #endif
35 
36 #include "wx/dde.h"
37 #include "wx/intl.h"
38 
39 #include "wx/msw/private.h"
40 
41 #include <string.h>
42 #include <ddeml.h>
43 
44 // ----------------------------------------------------------------------------
45 // macros and constants
46 // ----------------------------------------------------------------------------
47 
48 #ifdef __WIN32__
49     #define _EXPORT
50 #else
51     #define _EXPORT _export
52 #endif
53 
54 #if wxUSE_UNICODE
55     #define DDE_CP      CP_WINUNICODE
56 #else
57     #define DDE_CP      CP_WINANSI
58 #endif
59 
60 #define GetHConv()       ((HCONV)m_hConv)
61 
62 // default timeout for DDE operations (5sec)
63 #define DDE_TIMEOUT     5000
64 
65 // ----------------------------------------------------------------------------
66 // private functions
67 // ----------------------------------------------------------------------------
68 
69 static wxDDEConnection *DDEFindConnection(HCONV hConv);
70 static void DDEDeleteConnection(HCONV hConv);
71 static wxDDEServer *DDEFindServer(const wxString& s);
72 
73 extern "C" HDDEDATA EXPENTRY _EXPORT _DDECallback(WORD wType,
74                                                   WORD wFmt,
75                                                   HCONV hConv,
76                                                   HSZ hsz1,
77                                                   HSZ hsz2,
78                                                   HDDEDATA hData,
79                                                   DWORD lData1,
80                                                   DWORD lData2);
81 
82 // Add topic name to atom table before using in conversations
83 static HSZ DDEAddAtom(const wxString& string);
84 static HSZ DDEGetAtom(const wxString& string);
85 
86 // string handles
87 static HSZ DDEAtomFromString(const wxString& s);
88 static wxString DDEStringFromAtom(HSZ hsz);
89 static void DDEFreeString(HSZ hsz);
90 
91 // error handling
92 static wxString DDEGetErrorMsg(UINT error);
93 static void DDELogError(const wxString& s, UINT error = DMLERR_NO_ERROR);
94 
95 // ----------------------------------------------------------------------------
96 // global variables
97 // ----------------------------------------------------------------------------
98 
99 WX_DECLARE_STRING_HASH_MAP( HSZ, wxAtomMap );
100 
101 static DWORD DDEIdInst = 0L;
102 static wxDDEConnection *DDECurrentlyConnecting = NULL;
103 static wxAtomMap wxAtomTable;
104 
105 #include "wx/listimpl.cpp"
106 
107 WX_DEFINE_LIST(wxDDEClientList)
108 WX_DEFINE_LIST(wxDDEServerList)
109 WX_DEFINE_LIST(wxDDEConnectionList)
110 
111 static wxDDEClientList wxDDEClientObjects;
112 static wxDDEServerList wxDDEServerObjects;
113 
114 static bool DDEInitialized = false;
115 
116 // ----------------------------------------------------------------------------
117 // private classes
118 // ----------------------------------------------------------------------------
119 
120 // A module to allow DDE cleanup without calling these functions
121 // from app.cpp or from the user's application.
122 
123 class wxDDEModule : public wxModule
124 {
125 public:
wxDDEModule()126     wxDDEModule() {}
OnInit()127     bool OnInit() { return true; }
OnExit()128     void OnExit() { wxDDECleanUp(); }
129 
130 private:
131     DECLARE_DYNAMIC_CLASS(wxDDEModule)
132 };
133 
134 // ----------------------------------------------------------------------------
135 // wxWin macros
136 // ----------------------------------------------------------------------------
137 
IMPLEMENT_DYNAMIC_CLASS(wxDDEServer,wxServerBase)138 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer, wxServerBase)
139 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient, wxClientBase)
140 IMPLEMENT_CLASS(wxDDEConnection, wxConnectionBase)
141 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule, wxModule)
142 
143 // ============================================================================
144 // implementation
145 // ============================================================================
146 
147 // ----------------------------------------------------------------------------
148 // initialization and cleanup
149 // ----------------------------------------------------------------------------
150 
151 extern void wxDDEInitialize()
152 {
153     if ( !DDEInitialized )
154     {
155         // Should insert filter flags
156         PFNCALLBACK callback = (PFNCALLBACK)
157             MakeProcInstance((FARPROC)_DDECallback, wxGetInstance());
158         UINT rc = DdeInitialize(&DDEIdInst, callback, APPCLASS_STANDARD, 0L);
159         if ( rc != DMLERR_NO_ERROR )
160         {
161             DDELogError(_T("Failed to initialize DDE"), rc);
162         }
163         else
164         {
165             DDEInitialized = true;
166         }
167     }
168 }
169 
wxDDECleanUp()170 void wxDDECleanUp()
171 {
172     // deleting them later won't work as DDE won't be initialized any more
173     wxASSERT_MSG( wxDDEServerObjects.empty() &&
174                     wxDDEClientObjects.empty(),
175                     _T("all DDE objects should be deleted by now") );
176 
177     wxAtomTable.clear();
178 
179     if ( DDEIdInst != 0 )
180     {
181         DdeUninitialize(DDEIdInst);
182         DDEIdInst = 0;
183     }
184 }
185 
186 // ----------------------------------------------------------------------------
187 // functions working with the global connection list(s)
188 // ----------------------------------------------------------------------------
189 
190 // Global find connection
DDEFindConnection(HCONV hConv)191 static wxDDEConnection *DDEFindConnection(HCONV hConv)
192 {
193     wxDDEServerList::compatibility_iterator serverNode = wxDDEServerObjects.GetFirst();
194     wxDDEConnection *found = NULL;
195     while (serverNode && !found)
196     {
197         wxDDEServer *object = serverNode->GetData();
198         found = object->FindConnection((WXHCONV) hConv);
199         serverNode = serverNode->GetNext();
200     }
201 
202     if (found)
203     {
204         return found;
205     }
206 
207     wxDDEClientList::compatibility_iterator clientNode = wxDDEClientObjects.GetFirst();
208     while (clientNode && !found)
209     {
210         wxDDEClient *object = clientNode->GetData();
211         found = object->FindConnection((WXHCONV) hConv);
212         clientNode = clientNode->GetNext();
213     }
214     return found;
215 }
216 
217 // Global delete connection
DDEDeleteConnection(HCONV hConv)218 static void DDEDeleteConnection(HCONV hConv)
219 {
220     wxDDEServerList::compatibility_iterator serverNode = wxDDEServerObjects.GetFirst();
221     bool found = false;
222     while (serverNode && !found)
223     {
224         wxDDEServer *object = serverNode->GetData();
225         found = object->DeleteConnection((WXHCONV) hConv);
226         serverNode = serverNode->GetNext();
227     }
228     if (found)
229     {
230         return;
231     }
232 
233     wxDDEClientList::compatibility_iterator clientNode = wxDDEClientObjects.GetFirst();
234     while (clientNode && !found)
235     {
236         wxDDEClient *object = clientNode->GetData();
237         found = object->DeleteConnection((WXHCONV) hConv);
238         clientNode = clientNode->GetNext();
239     }
240 }
241 
242 // Find a server from a service name
DDEFindServer(const wxString & s)243 static wxDDEServer *DDEFindServer(const wxString& s)
244 {
245     wxDDEServerList::compatibility_iterator node = wxDDEServerObjects.GetFirst();
246     wxDDEServer *found = NULL;
247     while (node && !found)
248     {
249         wxDDEServer *object = node->GetData();
250 
251         if (object->GetServiceName() == s)
252         {
253             found = object;
254         }
255         else
256         {
257             node = node->GetNext();
258         }
259     }
260 
261     return found;
262 }
263 
264 // ----------------------------------------------------------------------------
265 // wxDDEServer
266 // ----------------------------------------------------------------------------
267 
wxDDEServer()268 wxDDEServer::wxDDEServer()
269 {
270     wxDDEInitialize();
271 
272     wxDDEServerObjects.Append(this);
273 }
274 
Create(const wxString & server)275 bool wxDDEServer::Create(const wxString& server)
276 {
277     m_serviceName = server;
278 
279     HSZ hsz = DDEAtomFromString(server);
280 
281     if ( !hsz )
282     {
283         return false;
284     }
285 
286 
287     bool success = (DdeNameService(DDEIdInst, hsz, (HSZ) NULL, DNS_REGISTER)
288         != NULL);
289 
290     if (!success)
291     {
292         DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
293             server.c_str()));
294     }
295 
296     DDEFreeString(hsz);
297 
298     return success;
299 }
300 
~wxDDEServer()301 wxDDEServer::~wxDDEServer()
302 {
303     if ( !m_serviceName.empty() )
304     {
305         HSZ hsz = DDEAtomFromString(m_serviceName);
306 
307         if (hsz)
308         {
309             if ( !DdeNameService(DDEIdInst, hsz,
310                 (HSZ) NULL, DNS_UNREGISTER) )
311             {
312                 DDELogError(wxString::Format(
313                     _("Failed to unregister DDE server '%s'"),
314                     m_serviceName.c_str()));
315             }
316 
317             DDEFreeString(hsz);
318         }
319     }
320 
321     wxDDEServerObjects.DeleteObject(this);
322 
323     wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
324     while (node)
325     {
326         wxDDEConnection *connection = node->GetData();
327         wxDDEConnectionList::compatibility_iterator next = node->GetNext();
328         connection->SetConnected(false);
329         connection->OnDisconnect(); // May delete the node implicitly
330         node = next;
331     }
332 
333     // If any left after this, delete them
334     node = m_connections.GetFirst();
335     while (node)
336     {
337         wxDDEConnection *connection = node->GetData();
338         wxDDEConnectionList::compatibility_iterator next = node->GetNext();
339         delete connection;
340         node = next;
341     }
342 }
343 
OnAcceptConnection(const wxString &)344 wxConnectionBase *wxDDEServer::OnAcceptConnection(const wxString& /* topic */)
345 {
346     return new wxDDEConnection;
347 }
348 
FindConnection(WXHCONV conv)349 wxDDEConnection *wxDDEServer::FindConnection(WXHCONV conv)
350 {
351     wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
352     wxDDEConnection *found = NULL;
353     while (node && !found)
354     {
355         wxDDEConnection *connection = node->GetData();
356         if (connection->m_hConv == conv)
357             found = connection;
358         else node = node->GetNext();
359     }
360     return found;
361 }
362 
363 // Only delete the entry in the map, not the actual connection
DeleteConnection(WXHCONV conv)364 bool wxDDEServer::DeleteConnection(WXHCONV conv)
365 {
366     wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
367     while (node)
368     {
369         wxDDEConnection *connection = node->GetData();
370         if (connection->m_hConv == conv)
371         {
372             m_connections.Erase(node);
373             return true;
374         }
375         else
376         {
377             node = node->GetNext();
378         }
379     }
380     return false;
381 }
382 
383 // ----------------------------------------------------------------------------
384 // wxDDEClient
385 // ----------------------------------------------------------------------------
386 
wxDDEClient()387 wxDDEClient::wxDDEClient()
388 {
389     wxDDEInitialize();
390 
391     wxDDEClientObjects.Append(this);
392 }
393 
~wxDDEClient()394 wxDDEClient::~wxDDEClient()
395 {
396     wxDDEClientObjects.DeleteObject(this);
397     wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
398     while (node)
399     {
400         wxDDEConnection *connection = node->GetData();
401         delete connection;  // Deletes the node implicitly (see ~wxDDEConnection)
402         node = m_connections.GetFirst();
403     }
404 }
405 
ValidHost(const wxString &)406 bool wxDDEClient::ValidHost(const wxString& /* host */)
407 {
408     return true;
409 }
410 
MakeConnection(const wxString & WXUNUSED (host),const wxString & server,const wxString & topic)411 wxConnectionBase *wxDDEClient::MakeConnection(const wxString& WXUNUSED(host),
412                                               const wxString& server,
413                                               const wxString& topic)
414 {
415     HSZ hszServer = DDEAtomFromString(server);
416 
417     if ( !hszServer )
418     {
419         return (wxConnectionBase*) NULL;
420     }
421 
422 
423     HSZ hszTopic = DDEAtomFromString(topic);
424 
425     if ( !hszTopic )
426     {
427         DDEFreeString(hszServer);
428         return (wxConnectionBase*) NULL;
429     }
430 
431 
432     HCONV hConv = ::DdeConnect(DDEIdInst, hszServer, hszTopic,
433         (PCONVCONTEXT) NULL);
434 
435     DDEFreeString(hszServer);
436     DDEFreeString(hszTopic);
437 
438 
439     if ( !hConv )
440     {
441         DDELogError( wxString::Format(
442             _("Failed to create connection to server '%s' on topic '%s'"),
443             server.c_str(), topic.c_str()) );
444     }
445     else
446     {
447         wxDDEConnection *connection = (wxDDEConnection*) OnMakeConnection();
448         if (connection)
449         {
450             connection->m_hConv = (WXHCONV) hConv;
451             connection->m_topicName = topic;
452             connection->m_client = this;
453             m_connections.Append(connection);
454             return connection;
455         }
456     }
457 
458     return (wxConnectionBase*) NULL;
459 }
460 
OnMakeConnection()461 wxConnectionBase *wxDDEClient::OnMakeConnection()
462 {
463     return new wxDDEConnection;
464 }
465 
FindConnection(WXHCONV conv)466 wxDDEConnection *wxDDEClient::FindConnection(WXHCONV conv)
467 {
468     wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
469     wxDDEConnection *found = NULL;
470     while (node && !found)
471     {
472         wxDDEConnection *connection = node->GetData();
473         if (connection->m_hConv == conv)
474             found = connection;
475         else node = node->GetNext();
476     }
477     return found;
478 }
479 
480 // Only delete the entry in the map, not the actual connection
DeleteConnection(WXHCONV conv)481 bool wxDDEClient::DeleteConnection(WXHCONV conv)
482 {
483     wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
484     while (node)
485     {
486         wxDDEConnection *connection = node->GetData();
487         if (connection->m_hConv == conv)
488         {
489             m_connections.Erase(node);
490             return true;
491         }
492         else node = node->GetNext();
493     }
494     return false;
495 }
496 
497 // ----------------------------------------------------------------------------
498 // wxDDEConnection
499 // ----------------------------------------------------------------------------
500 
wxDDEConnection(wxChar * buffer,int size)501 wxDDEConnection::wxDDEConnection(wxChar *buffer, int size)
502      : wxConnectionBase(buffer, size)
503 {
504     m_client = NULL;
505     m_server = NULL;
506 
507     m_hConv = 0;
508     m_sendingData = NULL;
509 }
510 
wxDDEConnection()511 wxDDEConnection::wxDDEConnection()
512      : wxConnectionBase()
513 {
514     m_hConv = 0;
515     m_sendingData = NULL;
516     m_server = NULL;
517     m_client = NULL;
518 }
519 
~wxDDEConnection()520 wxDDEConnection::~wxDDEConnection()
521 {
522     Disconnect();
523     if (m_server)
524         m_server->GetConnections().DeleteObject(this);
525     else
526         m_client->GetConnections().DeleteObject(this);
527 }
528 
529 // Calls that CLIENT can make
Disconnect()530 bool wxDDEConnection::Disconnect()
531 {
532     if ( !GetConnected() )
533         return true;
534 
535     DDEDeleteConnection(GetHConv());
536 
537     bool ok = DdeDisconnect(GetHConv()) != 0;
538     if ( !ok )
539     {
540         DDELogError(_T("Failed to disconnect from DDE server gracefully"));
541     }
542 
543     SetConnected( false );  // so we don't try and disconnect again
544 
545     return ok;
546 }
547 
Execute(const wxChar * data,int size,wxIPCFormat WXUNUSED (format))548 bool wxDDEConnection::Execute(const wxChar *data, int size, wxIPCFormat WXUNUSED(format))
549 {
550     DWORD result;
551     if (size < 0)
552     {
553         size = (wxStrlen(data) + 1) * sizeof(wxChar);    // includes final NUL
554     }
555 
556     bool ok = DdeClientTransaction((LPBYTE)data,
557                                     size,
558                                     GetHConv(),
559                                     NULL,
560 // If the transaction specified by the wType parameter does not pass data or is XTYP_EXECUTE,
561 // wFmt should be zero.
562                                     0,
563                                     XTYP_EXECUTE,
564                                     DDE_TIMEOUT,
565                                     &result) != 0;
566 
567     if ( !ok )
568     {
569         DDELogError(_T("DDE execute request failed"));
570     }
571 
572     return ok;
573 }
574 
Request(const wxString & item,int * size,wxIPCFormat format)575 wxChar *wxDDEConnection::Request(const wxString& item, int *size, wxIPCFormat format)
576 {
577     DWORD result;
578 
579     HSZ atom = DDEGetAtom(item);
580 
581     HDDEDATA returned_data = DdeClientTransaction(NULL, 0,
582                                                   GetHConv(),
583                                                   atom, format,
584                                                   XTYP_REQUEST,
585                                                   DDE_TIMEOUT,
586                                                   &result);
587     if ( !returned_data )
588     {
589         DDELogError(_T("DDE data request failed"));
590 
591         return NULL;
592     }
593 
594     DWORD len = DdeGetData(returned_data, NULL, 0, 0);
595 
596     wxChar *data = GetBufferAtLeast( len );
597     wxASSERT_MSG(data != NULL,
598                  _T("Buffer too small in wxDDEConnection::Request") );
599     (void) DdeGetData(returned_data, (LPBYTE)data, len, 0);
600 
601     (void) DdeFreeDataHandle(returned_data);
602 
603     if (size)
604         *size = (int)len;
605 
606     return data;
607 }
608 
Poke(const wxString & item,wxChar * data,int size,wxIPCFormat format)609 bool wxDDEConnection::Poke(const wxString& item, wxChar *data, int size, wxIPCFormat format)
610 {
611     DWORD result;
612     if (size < 0)
613     {
614         size = (wxStrlen(data) + 1) * sizeof(wxChar);    // includes final NUL
615     }
616 
617     HSZ item_atom = DDEGetAtom(item);
618     bool ok = DdeClientTransaction((LPBYTE)data,
619                                    size,
620                                    GetHConv(),
621                                    item_atom, format,
622                                    XTYP_POKE,
623                                    DDE_TIMEOUT,
624                                    &result) != 0;
625     if ( !ok )
626     {
627         DDELogError(_("DDE poke request failed"));
628     }
629 
630     return ok;
631 }
632 
StartAdvise(const wxString & item)633 bool wxDDEConnection::StartAdvise(const wxString& item)
634 {
635     DWORD result;
636     HSZ atom = DDEGetAtom(item);
637 
638     bool ok = DdeClientTransaction(NULL, 0,
639                                    GetHConv(),
640                                    atom, CF_TEXT,
641                                    XTYP_ADVSTART,
642                                    DDE_TIMEOUT,
643                                    &result) != 0;
644     if ( !ok )
645     {
646         DDELogError(_("Failed to establish an advise loop with DDE server"));
647     }
648 
649     return ok;
650 }
651 
StopAdvise(const wxString & item)652 bool wxDDEConnection::StopAdvise(const wxString& item)
653 {
654     DWORD result;
655     HSZ atom = DDEGetAtom(item);
656 
657     bool ok = DdeClientTransaction(NULL, 0,
658                                    GetHConv(),
659                                    atom, CF_TEXT,
660                                    XTYP_ADVSTOP,
661                                    DDE_TIMEOUT,
662                                    &result) != 0;
663     if ( !ok )
664     {
665         DDELogError(_("Failed to terminate the advise loop with DDE server"));
666     }
667 
668     return ok;
669 }
670 
671 // Calls that SERVER can make
Advise(const wxString & item,wxChar * data,int size,wxIPCFormat format)672 bool wxDDEConnection::Advise(const wxString& item,
673                              wxChar *data,
674                              int size,
675                              wxIPCFormat format)
676 {
677     if (size < 0)
678     {
679         size = (wxStrlen(data) + 1) * sizeof(wxChar);    // includes final NUL
680     }
681 
682     HSZ item_atom = DDEGetAtom(item);
683     HSZ topic_atom = DDEGetAtom(m_topicName);
684     m_sendingData = data;  // mrf: potential for scope problems here?
685     m_dataSize = size;
686     // wxIPC_PRIVATE does not succeed, so use text instead
687     m_dataType = format == wxIPC_PRIVATE ? wxIPC_TEXT : format;
688 
689     bool ok = DdePostAdvise(DDEIdInst, topic_atom, item_atom) != 0;
690     if ( !ok )
691     {
692         DDELogError(_("Failed to send DDE advise notification"));
693     }
694 
695     return ok;
696 }
697 
OnDisconnect()698 bool wxDDEConnection::OnDisconnect()
699 {
700     delete this;
701     return true;
702 }
703 
704 // ----------------------------------------------------------------------------
705 // _DDECallback
706 // ----------------------------------------------------------------------------
707 
708 #define DDERETURN HDDEDATA
709 
710 HDDEDATA EXPENTRY _EXPORT
_DDECallback(WORD wType,WORD wFmt,HCONV hConv,HSZ hsz1,HSZ hsz2,HDDEDATA hData,DWORD WXUNUSED (lData1),DWORD WXUNUSED (lData2))711 _DDECallback(WORD wType,
712              WORD wFmt,
713              HCONV hConv,
714              HSZ hsz1,
715              HSZ hsz2,
716              HDDEDATA hData,
717              DWORD WXUNUSED(lData1),
718              DWORD WXUNUSED(lData2))
719 {
720     switch (wType)
721     {
722         case XTYP_CONNECT:
723             {
724                 wxString topic = DDEStringFromAtom(hsz1),
725                          srv = DDEStringFromAtom(hsz2);
726                 wxDDEServer *server = DDEFindServer(srv);
727                 if (server)
728                 {
729                     wxDDEConnection *connection =
730                         (wxDDEConnection*) server->OnAcceptConnection(topic);
731                     if (connection)
732                     {
733                         connection->m_server = server;
734                         server->GetConnections().Append(connection);
735                         connection->m_hConv = 0;
736                         connection->m_topicName = topic;
737                         DDECurrentlyConnecting = connection;
738                         return (DDERETURN)(DWORD)true;
739                     }
740                 }
741                 break;
742             }
743 
744         case XTYP_CONNECT_CONFIRM:
745             {
746                 if (DDECurrentlyConnecting)
747                 {
748                     DDECurrentlyConnecting->m_hConv = (WXHCONV) hConv;
749                     DDECurrentlyConnecting = NULL;
750                     return (DDERETURN)(DWORD)true;
751                 }
752                 break;
753             }
754 
755         case XTYP_DISCONNECT:
756             {
757                 wxDDEConnection *connection = DDEFindConnection(hConv);
758                 if (connection)
759                 {
760                     connection->SetConnected( false );
761                     if (connection->OnDisconnect())
762                     {
763                         DDEDeleteConnection(hConv);  // Delete mapping: hConv => connection
764                         return (DDERETURN)(DWORD)true;
765                     }
766                 }
767                 break;
768             }
769 
770         case XTYP_EXECUTE:
771             {
772                 wxDDEConnection *connection = DDEFindConnection(hConv);
773 
774                 if (connection)
775                 {
776                     DWORD len = DdeGetData(hData, NULL, 0, 0);
777 
778                     wxChar *data = connection->GetBufferAtLeast( len );
779                     wxASSERT_MSG(data != NULL,
780                                  _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
781 
782                     DdeGetData(hData, (LPBYTE)data, len, 0);
783 
784                     DdeFreeDataHandle(hData);
785 
786 // XTYP_EXECUTE cannot be used for arbitrary data, but only for text
787                     if ( connection->OnExecute(connection->m_topicName,
788                                                data,
789                                                (int)len,
790                                                wxIPC_TEXT ) )
791                     {
792                         return (DDERETURN)(DWORD)DDE_FACK;
793                     }
794                 }
795 
796                 return (DDERETURN)DDE_FNOTPROCESSED;
797             }
798 
799         case XTYP_REQUEST:
800             {
801                 wxDDEConnection *connection = DDEFindConnection(hConv);
802 
803                 if (connection)
804                 {
805                     wxString item_name = DDEStringFromAtom(hsz2);
806 
807                     int user_size = -1;
808                     wxChar *data = connection->OnRequest(connection->m_topicName,
809                                                        item_name,
810                                                        &user_size,
811                                                        (wxIPCFormat) wFmt);
812                     if (data)
813                     {
814                         if (user_size < 0)
815                             user_size = (wxStrlen((wxChar*)data) + 1) * sizeof(wxChar);    // includes final NUL
816 
817                         HDDEDATA handle = DdeCreateDataHandle(DDEIdInst,
818                                                               (LPBYTE)data,
819                                                               user_size,
820                                                               0,
821                                                               hsz2,
822                                                               wFmt,
823                                                               0);
824                         return (DDERETURN)handle;
825                     }
826                 }
827                 break;
828             }
829 
830         case XTYP_POKE:
831             {
832                 wxDDEConnection *connection = DDEFindConnection(hConv);
833 
834                 if (connection)
835                 {
836                     wxString item_name = DDEStringFromAtom(hsz2);
837 
838                     DWORD len = DdeGetData(hData, NULL, 0, 0);
839 
840                     wxChar *data = connection->GetBufferAtLeast( len );
841                     wxASSERT_MSG(data != NULL,
842                                  _T("Buffer too small in _DDECallback (XTYP_POKE)") );
843 
844                     DdeGetData(hData, (LPBYTE)data, len, 0);
845 
846                     DdeFreeDataHandle(hData);
847 
848                     connection->OnPoke(connection->m_topicName,
849                                        item_name,
850                                        data,
851                                        (int)len,
852                                        (wxIPCFormat) wFmt);
853 
854                     return (DDERETURN)DDE_FACK;
855                 }
856                 else
857                 {
858                     return (DDERETURN)DDE_FNOTPROCESSED;
859                 }
860             }
861 
862         case XTYP_ADVSTART:
863             {
864                 wxDDEConnection *connection = DDEFindConnection(hConv);
865 
866                 if (connection)
867                 {
868                     wxString item_name = DDEStringFromAtom(hsz2);
869 
870                     return (DDERETURN)connection->
871                                 OnStartAdvise(connection->m_topicName, item_name);
872                 }
873 
874                 break;
875             }
876 
877         case XTYP_ADVSTOP:
878             {
879                 wxDDEConnection *connection = DDEFindConnection(hConv);
880 
881                 if (connection)
882                 {
883                     wxString item_name = DDEStringFromAtom(hsz2);
884 
885                     return (DDERETURN)connection->
886                         OnStopAdvise(connection->m_topicName, item_name);
887                 }
888 
889                 break;
890             }
891 
892         case XTYP_ADVREQ:
893             {
894                 wxDDEConnection *connection = DDEFindConnection(hConv);
895 
896                 if (connection && connection->m_sendingData)
897                 {
898                     HDDEDATA data = DdeCreateDataHandle
899                                     (
900                                         DDEIdInst,
901                                         (LPBYTE)connection->m_sendingData,
902                                         connection->m_dataSize,
903                                         0,
904                                         hsz2,
905                                         connection->m_dataType,
906                                         0
907                                     );
908 
909                     connection->m_sendingData = NULL;
910 
911                     return (DDERETURN)data;
912                 }
913 
914                 break;
915             }
916 
917         case XTYP_ADVDATA:
918             {
919                 wxDDEConnection *connection = DDEFindConnection(hConv);
920 
921                 if (connection)
922                 {
923                     wxString item_name = DDEStringFromAtom(hsz2);
924 
925                     DWORD len = DdeGetData(hData, NULL, 0, 0);
926 
927                     wxChar *data = connection->GetBufferAtLeast( len );
928                     wxASSERT_MSG(data != NULL,
929                                  _T("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
930 
931                     DdeGetData(hData, (LPBYTE)data, len, 0);
932 
933                     DdeFreeDataHandle(hData);
934                     if ( connection->OnAdvise(connection->m_topicName,
935                                               item_name,
936                                               data,
937                                               (int)len,
938                                               (wxIPCFormat) wFmt) )
939                     {
940                         return (DDERETURN)(DWORD)DDE_FACK;
941                     }
942                 }
943 
944                 return (DDERETURN)DDE_FNOTPROCESSED;
945             }
946     }
947 
948     return (DDERETURN)0;
949 }
950 
951 // ----------------------------------------------------------------------------
952 // DDE strings and atoms
953 // ----------------------------------------------------------------------------
954 
955 // Atom table stuff
DDEAddAtom(const wxString & str)956 static HSZ DDEAddAtom(const wxString& str)
957 {
958     HSZ atom = DDEAtomFromString(str);
959     wxAtomTable[str] = atom;
960     return atom;
961 }
962 
DDEGetAtom(const wxString & str)963 static HSZ DDEGetAtom(const wxString& str)
964 {
965     wxAtomMap::iterator it = wxAtomTable.find(str);
966 
967     if (it != wxAtomTable.end())
968         return it->second;
969 
970     return DDEAddAtom(str);
971 }
972 
973 /* atom <-> strings
974 The returned handle has to be freed by the caller (using
975 (static) DDEFreeString).
976 */
DDEAtomFromString(const wxString & s)977 static HSZ DDEAtomFromString(const wxString& s)
978 {
979     wxASSERT_MSG( DDEIdInst, _T("DDE not initialized") );
980 
981     HSZ hsz = DdeCreateStringHandle(DDEIdInst, (wxChar*) s.c_str(), DDE_CP);
982     if ( !hsz )
983     {
984         DDELogError(_("Failed to create DDE string"));
985     }
986 
987     return hsz;
988 }
989 
DDEStringFromAtom(HSZ hsz)990 static wxString DDEStringFromAtom(HSZ hsz)
991 {
992     // all DDE strings are normally limited to 255 bytes
993     static const size_t len = 256;
994 
995     wxString s;
996     (void)DdeQueryString(DDEIdInst, hsz, wxStringBuffer(s, len), len, DDE_CP);
997 
998     return s;
999 }
1000 
DDEFreeString(HSZ hsz)1001 static void DDEFreeString(HSZ hsz)
1002 {
1003     // DS: Failure to free a string handle might indicate there's
1004     // some other severe error.
1005     bool ok = (::DdeFreeStringHandle(DDEIdInst, hsz) != 0);
1006     wxASSERT_MSG( ok, wxT("Failed to free DDE string handle") );
1007     wxUnusedVar(ok);
1008 }
1009 
1010 // ----------------------------------------------------------------------------
1011 // error handling
1012 // ----------------------------------------------------------------------------
1013 
DDELogError(const wxString & s,UINT error)1014 static void DDELogError(const wxString& s, UINT error)
1015 {
1016     if ( !error )
1017     {
1018         error = DdeGetLastError(DDEIdInst);
1019     }
1020 
1021     wxLogError(s + _T(": ") + DDEGetErrorMsg(error));
1022 }
1023 
DDEGetErrorMsg(UINT error)1024 static wxString DDEGetErrorMsg(UINT error)
1025 {
1026     wxString err;
1027     switch ( error )
1028     {
1029         case DMLERR_NO_ERROR:
1030             err = _("no DDE error.");
1031             break;
1032 
1033         case DMLERR_ADVACKTIMEOUT:
1034             err = _("a request for a synchronous advise transaction has timed out.");
1035             break;
1036         case DMLERR_BUSY:
1037             err = _("the response to the transaction caused the DDE_FBUSY bit to be set.");
1038             break;
1039         case DMLERR_DATAACKTIMEOUT:
1040             err = _("a request for a synchronous data transaction has timed out.");
1041             break;
1042         case DMLERR_DLL_NOT_INITIALIZED:
1043             err = _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
1044             break;
1045         case DMLERR_DLL_USAGE:
1046             err = _("an application initialized as APPCLASS_MONITOR has\nattempted to perform a DDE transaction,\nor an application initialized as APPCMD_CLIENTONLY has \nattempted to perform server transactions.");
1047             break;
1048         case DMLERR_EXECACKTIMEOUT:
1049             err = _("a request for a synchronous execute transaction has timed out.");
1050             break;
1051         case DMLERR_INVALIDPARAMETER:
1052             err = _("a parameter failed to be validated by the DDEML.");
1053             break;
1054         case DMLERR_LOW_MEMORY:
1055             err = _("a DDEML application has created a prolonged race condition.");
1056             break;
1057         case DMLERR_MEMORY_ERROR:
1058             err = _("a memory allocation failed.");
1059             break;
1060         case DMLERR_NO_CONV_ESTABLISHED:
1061             err = _("a client's attempt to establish a conversation has failed.");
1062             break;
1063         case DMLERR_NOTPROCESSED:
1064             err = _("a transaction failed.");
1065             break;
1066         case DMLERR_POKEACKTIMEOUT:
1067             err = _("a request for a synchronous poke transaction has timed out.");
1068             break;
1069         case DMLERR_POSTMSG_FAILED:
1070             err = _("an internal call to the PostMessage function has failed. ");
1071             break;
1072         case DMLERR_REENTRANCY:
1073             err = _("reentrancy problem.");
1074             break;
1075         case DMLERR_SERVER_DIED:
1076             err = _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1077             break;
1078         case DMLERR_SYS_ERROR:
1079             err = _("an internal error has occurred in the DDEML.");
1080             break;
1081         case DMLERR_UNADVACKTIMEOUT:
1082             err = _("a request to end an advise transaction has timed out.");
1083             break;
1084         case DMLERR_UNFOUND_QUEUE_ID:
1085             err = _("an invalid transaction identifier was passed to a DDEML function.\nOnce the application has returned from an XTYP_XACT_COMPLETE callback,\nthe transaction identifier for that callback is no longer valid.");
1086             break;
1087         default:
1088             err.Printf(_("Unknown DDE error %08x"), error);
1089     }
1090 
1091     return err;
1092 }
1093 
1094 #endif
1095   // wxUSE_IPC
1096