1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/dde.cpp
3 // Purpose:     DDE classes
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     01/02/97
7 // Copyright:   (c) Julian Smart
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21 
22 #ifdef __BORLANDC__
23     #pragma hdrstop
24 #endif
25 
26 #if wxUSE_IPC
27 
28 #ifndef WX_PRECOMP
29     #include "wx/utils.h"
30     #include "wx/app.h"
31     #include "wx/hashmap.h"
32     #include "wx/module.h"
33 #endif
34 
35 #include "wx/dde.h"
36 #include "wx/intl.h"
37 #include "wx/buffer.h"
38 #include "wx/strconv.h"
39 
40 #include "wx/msw/private.h"
41 
42 #include <string.h>
43 #include <ddeml.h>
44 
45 // ----------------------------------------------------------------------------
46 // macros and constants
47 // ----------------------------------------------------------------------------
48 
49 #ifdef __WIN32__
50     #define _EXPORT
51 #else
52     #define _EXPORT _export
53 #endif
54 
55 #if wxUSE_UNICODE
56     #define DDE_CP      CP_WINUNICODE
57 #else
58     #define DDE_CP      CP_WINANSI
59 #endif
60 
61 #define GetHConv()       ((HCONV)m_hConv)
62 
63 // default timeout for DDE operations (5sec)
64 #define DDE_TIMEOUT     5000
65 
66 // ----------------------------------------------------------------------------
67 // private functions
68 // ----------------------------------------------------------------------------
69 
70 static wxDDEConnection *DDEFindConnection(HCONV hConv);
71 static void DDEDeleteConnection(HCONV hConv);
72 static wxDDEServer *DDEFindServer(const wxString& s);
73 
74 extern "C" HDDEDATA EXPENTRY _EXPORT _DDECallback(WORD wType,
75                                                   WORD wFmt,
76                                                   HCONV hConv,
77                                                   HSZ hsz1,
78                                                   HSZ hsz2,
79                                                   HDDEDATA hData,
80                                                   DWORD lData1,
81                                                   DWORD lData2);
82 
83 // Add topic name to atom table before using in conversations
84 static HSZ DDEAddAtom(const wxString& string);
85 static HSZ DDEGetAtom(const wxString& string);
86 
87 // string handles
88 static HSZ DDEAtomFromString(const wxString& s);
89 static wxString DDEStringFromAtom(HSZ hsz);
90 static void DDEFreeString(HSZ hsz);
91 
92 // error handling
93 static wxString DDEGetErrorMsg(UINT error);
94 static void DDELogError(const wxString& s, UINT error = DMLERR_NO_ERROR);
95 
96 // ----------------------------------------------------------------------------
97 // global variables
98 // ----------------------------------------------------------------------------
99 
100 WX_DECLARE_STRING_HASH_MAP( HSZ, wxAtomMap );
101 
102 static DWORD DDEIdInst = 0L;
103 static wxDDEConnection *DDECurrentlyConnecting = NULL;
104 static wxAtomMap wxAtomTable;
105 
106 #include "wx/listimpl.cpp"
107 
108 WX_DEFINE_LIST(wxDDEClientList)
109 WX_DEFINE_LIST(wxDDEServerList)
110 WX_DEFINE_LIST(wxDDEConnectionList)
111 
112 static wxDDEClientList wxDDEClientObjects;
113 static wxDDEServerList wxDDEServerObjects;
114 
115 static bool DDEInitialized = false;
116 
117 // ----------------------------------------------------------------------------
118 // private classes
119 // ----------------------------------------------------------------------------
120 
121 // A module to allow DDE cleanup without calling these functions
122 // from app.cpp or from the user's application.
123 
124 class wxDDEModule : public wxModule
125 {
126 public:
wxDDEModule()127     wxDDEModule() {}
OnInit()128     bool OnInit() { return true; }
OnExit()129     void OnExit() { wxDDECleanUp(); }
130 
131 private:
132     DECLARE_DYNAMIC_CLASS(wxDDEModule)
133 };
134 
135 // ----------------------------------------------------------------------------
136 // wxWin macros
137 // ----------------------------------------------------------------------------
138 
IMPLEMENT_DYNAMIC_CLASS(wxDDEServer,wxServerBase)139 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer, wxServerBase)
140 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient, wxClientBase)
141 IMPLEMENT_DYNAMIC_CLASS(wxDDEConnection, wxConnectionBase)
142 IMPLEMENT_DYNAMIC_CLASS(wxDDEModule, wxModule)
143 
144 // ============================================================================
145 // implementation
146 // ============================================================================
147 
148 // ----------------------------------------------------------------------------
149 // initialization and cleanup
150 // ----------------------------------------------------------------------------
151 
152 extern void wxDDEInitialize()
153 {
154     if ( !DDEInitialized )
155     {
156         // Should insert filter flags
157         PFNCALLBACK callback = (PFNCALLBACK)
158             MakeProcInstance((FARPROC)_DDECallback, wxGetInstance());
159         UINT rc = DdeInitialize(&DDEIdInst, callback, APPCLASS_STANDARD, 0L);
160         if ( rc != DMLERR_NO_ERROR )
161         {
162             DDELogError(wxT("Failed to initialize DDE"), rc);
163         }
164         else
165         {
166             DDEInitialized = true;
167         }
168     }
169 }
170 
wxDDECleanUp()171 void wxDDECleanUp()
172 {
173     // deleting them later won't work as DDE won't be initialized any more
174     wxASSERT_MSG( wxDDEServerObjects.empty() &&
175                     wxDDEClientObjects.empty(),
176                     wxT("all DDE objects should be deleted by now") );
177 
178     wxAtomTable.clear();
179 
180     if ( DDEIdInst != 0 )
181     {
182         DdeUninitialize(DDEIdInst);
183         DDEIdInst = 0;
184     }
185 }
186 
187 // ----------------------------------------------------------------------------
188 // functions working with the global connection list(s)
189 // ----------------------------------------------------------------------------
190 
191 // Global find connection
DDEFindConnection(HCONV hConv)192 static wxDDEConnection *DDEFindConnection(HCONV hConv)
193 {
194     wxDDEServerList::compatibility_iterator serverNode = wxDDEServerObjects.GetFirst();
195     wxDDEConnection *found = NULL;
196     while (serverNode && !found)
197     {
198         wxDDEServer *object = serverNode->GetData();
199         found = object->FindConnection((WXHCONV) hConv);
200         serverNode = serverNode->GetNext();
201     }
202 
203     if (found)
204     {
205         return found;
206     }
207 
208     wxDDEClientList::compatibility_iterator clientNode = wxDDEClientObjects.GetFirst();
209     while (clientNode && !found)
210     {
211         wxDDEClient *object = clientNode->GetData();
212         found = object->FindConnection((WXHCONV) hConv);
213         clientNode = clientNode->GetNext();
214     }
215     return found;
216 }
217 
218 // Global delete connection
DDEDeleteConnection(HCONV hConv)219 static void DDEDeleteConnection(HCONV hConv)
220 {
221     wxDDEServerList::compatibility_iterator serverNode = wxDDEServerObjects.GetFirst();
222     bool found = false;
223     while (serverNode && !found)
224     {
225         wxDDEServer *object = serverNode->GetData();
226         found = object->DeleteConnection((WXHCONV) hConv);
227         serverNode = serverNode->GetNext();
228     }
229     if (found)
230     {
231         return;
232     }
233 
234     wxDDEClientList::compatibility_iterator clientNode = wxDDEClientObjects.GetFirst();
235     while (clientNode && !found)
236     {
237         wxDDEClient *object = clientNode->GetData();
238         found = object->DeleteConnection((WXHCONV) hConv);
239         clientNode = clientNode->GetNext();
240     }
241 }
242 
243 // Find a server from a service name
DDEFindServer(const wxString & s)244 static wxDDEServer *DDEFindServer(const wxString& s)
245 {
246     wxDDEServerList::compatibility_iterator node = wxDDEServerObjects.GetFirst();
247     wxDDEServer *found = NULL;
248     while (node && !found)
249     {
250         wxDDEServer *object = node->GetData();
251 
252         if (object->GetServiceName() == s)
253         {
254             found = object;
255         }
256         else
257         {
258             node = node->GetNext();
259         }
260     }
261 
262     return found;
263 }
264 
265 // ----------------------------------------------------------------------------
266 // wxDDEServer
267 // ----------------------------------------------------------------------------
268 
wxDDEServer()269 wxDDEServer::wxDDEServer()
270 {
271     wxDDEInitialize();
272 
273     wxDDEServerObjects.Append(this);
274 }
275 
Create(const wxString & server)276 bool wxDDEServer::Create(const wxString& server)
277 {
278     m_serviceName = server;
279 
280     HSZ hsz = DDEAtomFromString(server);
281 
282     if ( !hsz )
283     {
284         return false;
285     }
286 
287 
288     bool success = (DdeNameService(DDEIdInst, hsz, (HSZ) NULL, DNS_REGISTER)
289         != NULL);
290 
291     if (!success)
292     {
293         DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
294             server.c_str()));
295     }
296 
297     DDEFreeString(hsz);
298 
299     return success;
300 }
301 
~wxDDEServer()302 wxDDEServer::~wxDDEServer()
303 {
304     if ( !m_serviceName.empty() )
305     {
306         HSZ hsz = DDEAtomFromString(m_serviceName);
307 
308         if (hsz)
309         {
310             if ( !DdeNameService(DDEIdInst, hsz,
311                 (HSZ) NULL, DNS_UNREGISTER) )
312             {
313                 DDELogError(wxString::Format(
314                     _("Failed to unregister DDE server '%s'"),
315                     m_serviceName.c_str()));
316             }
317 
318             DDEFreeString(hsz);
319         }
320     }
321 
322     wxDDEServerObjects.DeleteObject(this);
323 
324     wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
325     while (node)
326     {
327         wxDDEConnection *connection = node->GetData();
328         wxDDEConnectionList::compatibility_iterator next = node->GetNext();
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 NULL;
420     }
421 
422 
423     HSZ hszTopic = DDEAtomFromString(topic);
424 
425     if ( !hszTopic )
426     {
427         DDEFreeString(hszServer);
428         return 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 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(void * buffer,size_t size)501 wxDDEConnection::wxDDEConnection(void *buffer, size_t 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(wxT("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 
548 bool
DoExecute(const void * data,size_t size,wxIPCFormat format)549 wxDDEConnection::DoExecute(const void *data, size_t size, wxIPCFormat format)
550 {
551     wxCHECK_MSG( format == wxIPC_TEXT ||
552                  format == wxIPC_UTF8TEXT ||
553                  format == wxIPC_UNICODETEXT,
554                  false,
555                  wxT("wxDDEServer::Execute() supports only text data") );
556 
557     wxMemoryBuffer buffer;
558     LPBYTE realData = NULL;
559     size_t realSize = 0;
560     wxMBConv *conv = NULL;
561 
562     // Windows only supports either ANSI or UTF-16 format depending on the
563     // build, so we need to convert the data if it doesn't use it already
564 #if wxUSE_UNICODE
565     if ( format == wxIPC_TEXT )
566     {
567         conv = &wxConvLibc;
568     }
569     else if ( format == wxIPC_UTF8TEXT )
570     {
571         conv = &wxConvUTF8;
572     }
573     else // no conversion necessary for wxIPC_UNICODETEXT
574     {
575         realData = (LPBYTE)data;
576         realSize = size;
577     }
578 
579     if ( conv )
580     {
581         const char * const text = (const char *)data;
582         const size_t len = size;
583 
584         realSize = conv->ToWChar(NULL, 0, text, len);
585         if ( realSize == wxCONV_FAILED )
586             return false;
587 
588         realData = (LPBYTE)buffer.GetWriteBuf(realSize*sizeof(wchar_t));
589         if ( !realData )
590             return false;
591 
592         realSize = conv->ToWChar((wchar_t *)realData, realSize, text, len);
593         if ( realSize == wxCONV_FAILED )
594             return false;
595 
596         // We need to pass the size of the buffer to DdeClientTransaction() and
597         // not the length of the string.
598         realSize *= sizeof(wchar_t);
599     }
600 #else // !wxUSE_UNICODE
601     if ( format == wxIPC_UNICODETEXT )
602     {
603         conv = &wxConvLibc;
604     }
605     else if ( format == wxIPC_UTF8TEXT )
606     {
607         // we could implement this in theory but it's not obvious how to pass
608         // the format information and, basically, why bother -- just use
609         // Unicode build
610         wxFAIL_MSG( wxT("UTF-8 text not supported in ANSI build") );
611 
612         return false;
613     }
614     else // don't convert wxIPC_TEXT
615     {
616         realData = (LPBYTE)data;
617         realSize = size;
618     }
619 
620     if ( conv )
621     {
622         const wchar_t * const wtext = (const wchar_t *)data;
623         const size_t len = size/sizeof(wchar_t);
624 
625         realSize = conv->FromWChar(NULL, 0, wtext, len);
626         if ( realSize == wxCONV_FAILED )
627             return false;
628 
629         realData = (LPBYTE)buffer.GetWriteBuf(realSize);
630         if ( !realData )
631             return false;
632 
633         realSize = conv->FromWChar((char*)realData, realSize, wtext, len);
634         if ( realSize == wxCONV_FAILED )
635             return false;
636     }
637 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
638 
639     DWORD result;
640     bool ok = DdeClientTransaction(realData,
641                                    realSize,
642                                    GetHConv(),
643                                    NULL,
644                                    // MSDN: if the transaction specified by
645                                    // the wType parameter does not pass data
646                                    // or is XTYP_EXECUTE, wFmt should be zero.
647                                    0,
648                                    XTYP_EXECUTE,
649                                    DDE_TIMEOUT,
650                                    &result) != 0;
651 
652     if ( !ok )
653     {
654         DDELogError(wxT("DDE execute request failed"));
655     }
656 
657     return ok;
658 }
659 
Request(const wxString & item,size_t * size,wxIPCFormat format)660 const void *wxDDEConnection::Request(const wxString& item, size_t *size, wxIPCFormat format)
661 {
662     DWORD result;
663 
664     HSZ atom = DDEGetAtom(item);
665 
666     HDDEDATA returned_data = DdeClientTransaction(NULL, 0,
667                                                   GetHConv(),
668                                                   atom, format,
669                                                   XTYP_REQUEST,
670                                                   DDE_TIMEOUT,
671                                                   &result);
672     if ( !returned_data )
673     {
674         DDELogError(wxT("DDE data request failed"));
675 
676         return NULL;
677     }
678 
679     DWORD len = DdeGetData(returned_data, NULL, 0, 0);
680 
681     void *data = GetBufferAtLeast(len);
682     wxASSERT_MSG(data != NULL,
683                  wxT("Buffer too small in wxDDEConnection::Request") );
684     (void) DdeGetData(returned_data, (LPBYTE)data, len, 0);
685 
686     (void) DdeFreeDataHandle(returned_data);
687 
688     if (size)
689         *size = (size_t)len;
690 
691     return data;
692 }
693 
DoPoke(const wxString & item,const void * data,size_t size,wxIPCFormat format)694 bool wxDDEConnection::DoPoke(const wxString& item, const void *data, size_t size, wxIPCFormat format)
695 {
696     DWORD result;
697 
698     HSZ item_atom = DDEGetAtom(item);
699     bool ok = DdeClientTransaction((LPBYTE)data,
700                                    size,
701                                    GetHConv(),
702                                    item_atom, format,
703                                    XTYP_POKE,
704                                    DDE_TIMEOUT,
705                                    &result) != 0;
706     if ( !ok )
707     {
708         DDELogError(_("DDE poke request failed"));
709     }
710 
711     return ok;
712 }
713 
StartAdvise(const wxString & item)714 bool wxDDEConnection::StartAdvise(const wxString& item)
715 {
716     DWORD result;
717     HSZ atom = DDEGetAtom(item);
718 
719     bool ok = DdeClientTransaction(NULL, 0,
720                                    GetHConv(),
721                                    atom, CF_TEXT,
722                                    XTYP_ADVSTART,
723                                    DDE_TIMEOUT,
724                                    &result) != 0;
725     if ( !ok )
726     {
727         DDELogError(_("Failed to establish an advise loop with DDE server"));
728     }
729 
730     return ok;
731 }
732 
StopAdvise(const wxString & item)733 bool wxDDEConnection::StopAdvise(const wxString& item)
734 {
735     DWORD result;
736     HSZ atom = DDEGetAtom(item);
737 
738     bool ok = DdeClientTransaction(NULL, 0,
739                                    GetHConv(),
740                                    atom, CF_TEXT,
741                                    XTYP_ADVSTOP,
742                                    DDE_TIMEOUT,
743                                    &result) != 0;
744     if ( !ok )
745     {
746         DDELogError(_("Failed to terminate the advise loop with DDE server"));
747     }
748 
749     return ok;
750 }
751 
752 // Calls that SERVER can make
DoAdvise(const wxString & item,const void * data,size_t size,wxIPCFormat format)753 bool wxDDEConnection::DoAdvise(const wxString& item,
754                                const void *data,
755                                size_t size,
756                                wxIPCFormat format)
757 {
758     HSZ item_atom = DDEGetAtom(item);
759     HSZ topic_atom = DDEGetAtom(m_topicName);
760     m_sendingData = data;  // mrf: potential for scope problems here?
761     m_dataSize = size;
762     // wxIPC_PRIVATE does not succeed, so use text instead
763     m_dataType = format == wxIPC_PRIVATE ? wxIPC_TEXT : format;
764 
765     bool ok = DdePostAdvise(DDEIdInst, topic_atom, item_atom) != 0;
766     if ( !ok )
767     {
768         DDELogError(_("Failed to send DDE advise notification"));
769     }
770 
771     return ok;
772 }
773 
774 // ----------------------------------------------------------------------------
775 // _DDECallback
776 // ----------------------------------------------------------------------------
777 
778 #define DDERETURN HDDEDATA
779 
780 HDDEDATA EXPENTRY _EXPORT
_DDECallback(WORD wType,WORD wFmt,HCONV hConv,HSZ hsz1,HSZ hsz2,HDDEDATA hData,DWORD WXUNUSED (lData1),DWORD WXUNUSED (lData2))781 _DDECallback(WORD wType,
782              WORD wFmt,
783              HCONV hConv,
784              HSZ hsz1,
785              HSZ hsz2,
786              HDDEDATA hData,
787              DWORD WXUNUSED(lData1),
788              DWORD WXUNUSED(lData2))
789 {
790     switch (wType)
791     {
792         case XTYP_CONNECT:
793             {
794                 wxString topic = DDEStringFromAtom(hsz1),
795                          srv = DDEStringFromAtom(hsz2);
796                 wxDDEServer *server = DDEFindServer(srv);
797                 if (server)
798                 {
799                     wxDDEConnection *connection =
800                         (wxDDEConnection*) server->OnAcceptConnection(topic);
801                     if (connection)
802                     {
803                         connection->m_server = server;
804                         server->GetConnections().Append(connection);
805                         connection->m_hConv = 0;
806                         connection->m_topicName = topic;
807                         DDECurrentlyConnecting = connection;
808                         return (DDERETURN)(DWORD)true;
809                     }
810                 }
811                 break;
812             }
813 
814         case XTYP_CONNECT_CONFIRM:
815             {
816                 if (DDECurrentlyConnecting)
817                 {
818                     DDECurrentlyConnecting->m_hConv = (WXHCONV) hConv;
819                     DDECurrentlyConnecting = NULL;
820                     return (DDERETURN)(DWORD)true;
821                 }
822                 break;
823             }
824 
825         case XTYP_DISCONNECT:
826             {
827                 wxDDEConnection *connection = DDEFindConnection(hConv);
828                 if (connection)
829                 {
830                     connection->SetConnected( false );
831                     if (connection->OnDisconnect())
832                     {
833                         DDEDeleteConnection(hConv);  // Delete mapping: hConv => connection
834                         return (DDERETURN)(DWORD)true;
835                     }
836                 }
837                 break;
838             }
839 
840         case XTYP_EXECUTE:
841             {
842                 wxDDEConnection *connection = DDEFindConnection(hConv);
843 
844                 if (connection)
845                 {
846                     DWORD len = DdeGetData(hData, NULL, 0, 0);
847 
848                     void *data = connection->GetBufferAtLeast(len);
849                     wxASSERT_MSG(data != NULL,
850                                  wxT("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
851 
852                     DdeGetData(hData, (LPBYTE)data, len, 0);
853 
854                     DdeFreeDataHandle(hData);
855 
856                     // XTYP_EXECUTE can be used for text only and the text is
857                     // always in ANSI format for ANSI build and Unicode format
858                     // in Unicode build
859                     #if wxUSE_UNICODE
860                         wFmt = wxIPC_UNICODETEXT;
861                     #else
862                         wFmt = wxIPC_TEXT;
863                     #endif
864 
865                     if ( connection->OnExecute(connection->m_topicName,
866                                                data,
867                                                (int)len,
868                                                (wxIPCFormat)wFmt) )
869                     {
870                         return (DDERETURN)(DWORD)DDE_FACK;
871                     }
872                 }
873 
874                 return (DDERETURN)DDE_FNOTPROCESSED;
875             }
876 
877         case XTYP_REQUEST:
878             {
879                 wxDDEConnection *connection = DDEFindConnection(hConv);
880 
881                 if (connection)
882                 {
883                     wxString item_name = DDEStringFromAtom(hsz2);
884 
885                     size_t user_size = wxNO_LEN;
886                     const void *data = connection->OnRequest(connection->m_topicName,
887                                                              item_name,
888                                                              &user_size,
889                                                              (wxIPCFormat)wFmt);
890                     if (data)
891                     {
892                       if (user_size == wxNO_LEN)
893                         switch (wFmt)
894                         {
895                           case wxIPC_TEXT:
896                           case wxIPC_UTF8TEXT:
897                             user_size = strlen((const char*)data) + 1;  // includes final NUL
898                             break;
899                           case wxIPC_UNICODETEXT:
900                             user_size = (wcslen((const wchar_t*)data) + 1) * sizeof(wchar_t);  // includes final NUL
901                             break;
902                           default:
903                             user_size = 0;
904                         }
905 
906                         HDDEDATA handle = DdeCreateDataHandle(DDEIdInst,
907                                                               (LPBYTE)data,
908                                                               user_size,
909                                                               0,
910                                                               hsz2,
911                                                               wFmt,
912                                                               0);
913                         return (DDERETURN)handle;
914                     }
915                 }
916                 break;
917             }
918 
919         case XTYP_POKE:
920             {
921                 wxDDEConnection *connection = DDEFindConnection(hConv);
922 
923                 if (connection)
924                 {
925                     wxString item_name = DDEStringFromAtom(hsz2);
926 
927                     DWORD len = DdeGetData(hData, NULL, 0, 0);
928 
929                     void *data = connection->GetBufferAtLeast(len);
930                     wxASSERT_MSG(data != NULL,
931                                  wxT("Buffer too small in _DDECallback (XTYP_POKE)") );
932 
933                     DdeGetData(hData, (LPBYTE)data, len, 0);
934 
935                     DdeFreeDataHandle(hData);
936 
937                     connection->OnPoke(connection->m_topicName,
938                                        item_name,
939                                        data,
940                                        (int)len,
941                                        (wxIPCFormat) wFmt);
942 
943                     return (DDERETURN)DDE_FACK;
944                 }
945                 else
946                 {
947                     return (DDERETURN)DDE_FNOTPROCESSED;
948                 }
949             }
950 
951         case XTYP_ADVSTART:
952             {
953                 wxDDEConnection *connection = DDEFindConnection(hConv);
954 
955                 if (connection)
956                 {
957                     wxString item_name = DDEStringFromAtom(hsz2);
958 
959                     return (DDERETURN)connection->
960                                 OnStartAdvise(connection->m_topicName, item_name);
961                 }
962 
963                 break;
964             }
965 
966         case XTYP_ADVSTOP:
967             {
968                 wxDDEConnection *connection = DDEFindConnection(hConv);
969 
970                 if (connection)
971                 {
972                     wxString item_name = DDEStringFromAtom(hsz2);
973 
974                     return (DDERETURN)connection->
975                         OnStopAdvise(connection->m_topicName, item_name);
976                 }
977 
978                 break;
979             }
980 
981         case XTYP_ADVREQ:
982             {
983                 wxDDEConnection *connection = DDEFindConnection(hConv);
984 
985                 if (connection && connection->m_sendingData)
986                 {
987                     HDDEDATA data = DdeCreateDataHandle
988                                     (
989                                         DDEIdInst,
990                                         (LPBYTE)connection->m_sendingData,
991                                         connection->m_dataSize,
992                                         0,
993                                         hsz2,
994                                         connection->m_dataType,
995                                         0
996                                     );
997 
998                     connection->m_sendingData = NULL;
999 
1000                     return (DDERETURN)data;
1001                 }
1002 
1003                 break;
1004             }
1005 
1006         case XTYP_ADVDATA:
1007             {
1008                 wxDDEConnection *connection = DDEFindConnection(hConv);
1009 
1010                 if (connection)
1011                 {
1012                     wxString item_name = DDEStringFromAtom(hsz2);
1013 
1014                     DWORD len = DdeGetData(hData, NULL, 0, 0);
1015 
1016                     void *data = connection->GetBufferAtLeast(len);
1017                     wxASSERT_MSG(data != NULL,
1018                                  wxT("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
1019 
1020                     DdeGetData(hData, (LPBYTE)data, len, 0);
1021 
1022                     DdeFreeDataHandle(hData);
1023                     if ( connection->OnAdvise(connection->m_topicName,
1024                                               item_name,
1025                                               data,
1026                                               (int)len,
1027                                               (wxIPCFormat) wFmt) )
1028                     {
1029                         return (DDERETURN)(DWORD)DDE_FACK;
1030                     }
1031                 }
1032 
1033                 return (DDERETURN)DDE_FNOTPROCESSED;
1034             }
1035     }
1036 
1037     return (DDERETURN)0;
1038 }
1039 
1040 // ----------------------------------------------------------------------------
1041 // DDE strings and atoms
1042 // ----------------------------------------------------------------------------
1043 
1044 // Atom table stuff
DDEAddAtom(const wxString & str)1045 static HSZ DDEAddAtom(const wxString& str)
1046 {
1047     HSZ atom = DDEAtomFromString(str);
1048     wxAtomTable[str] = atom;
1049     return atom;
1050 }
1051 
DDEGetAtom(const wxString & str)1052 static HSZ DDEGetAtom(const wxString& str)
1053 {
1054     wxAtomMap::iterator it = wxAtomTable.find(str);
1055 
1056     if (it != wxAtomTable.end())
1057         return it->second;
1058 
1059     return DDEAddAtom(str);
1060 }
1061 
1062 /* atom <-> strings
1063 The returned handle has to be freed by the caller (using
1064 (static) DDEFreeString).
1065 */
DDEAtomFromString(const wxString & s)1066 static HSZ DDEAtomFromString(const wxString& s)
1067 {
1068     wxASSERT_MSG( DDEIdInst, wxT("DDE not initialized") );
1069 
1070     HSZ hsz = DdeCreateStringHandle(DDEIdInst, wxMSW_CONV_LPTSTR(s), DDE_CP);
1071     if ( !hsz )
1072     {
1073         DDELogError(_("Failed to create DDE string"));
1074     }
1075 
1076     return hsz;
1077 }
1078 
DDEStringFromAtom(HSZ hsz)1079 static wxString DDEStringFromAtom(HSZ hsz)
1080 {
1081     // all DDE strings are normally limited to 255 bytes
1082     static const size_t len = 256;
1083 
1084     wxString s;
1085     (void)DdeQueryString(DDEIdInst, hsz, wxStringBuffer(s, len), len, DDE_CP);
1086 
1087     return s;
1088 }
1089 
DDEFreeString(HSZ hsz)1090 static void DDEFreeString(HSZ hsz)
1091 {
1092     // DS: Failure to free a string handle might indicate there's
1093     // some other severe error.
1094     bool ok = (::DdeFreeStringHandle(DDEIdInst, hsz) != 0);
1095     wxASSERT_MSG( ok, wxT("Failed to free DDE string handle") );
1096     wxUnusedVar(ok);
1097 }
1098 
1099 // ----------------------------------------------------------------------------
1100 // error handling
1101 // ----------------------------------------------------------------------------
1102 
DDELogError(const wxString & s,UINT error)1103 static void DDELogError(const wxString& s, UINT error)
1104 {
1105     if ( !error )
1106     {
1107         error = DdeGetLastError(DDEIdInst);
1108     }
1109 
1110     wxLogError(s + wxT(": ") + DDEGetErrorMsg(error));
1111 }
1112 
DDEGetErrorMsg(UINT error)1113 static wxString DDEGetErrorMsg(UINT error)
1114 {
1115     wxString err;
1116     switch ( error )
1117     {
1118         case DMLERR_NO_ERROR:
1119             err = _("no DDE error.");
1120             break;
1121 
1122         case DMLERR_ADVACKTIMEOUT:
1123             err = _("a request for a synchronous advise transaction has timed out.");
1124             break;
1125         case DMLERR_BUSY:
1126             err = _("the response to the transaction caused the DDE_FBUSY bit to be set.");
1127             break;
1128         case DMLERR_DATAACKTIMEOUT:
1129             err = _("a request for a synchronous data transaction has timed out.");
1130             break;
1131         case DMLERR_DLL_NOT_INITIALIZED:
1132             err = _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
1133             break;
1134         case DMLERR_DLL_USAGE:
1135             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.");
1136             break;
1137         case DMLERR_EXECACKTIMEOUT:
1138             err = _("a request for a synchronous execute transaction has timed out.");
1139             break;
1140         case DMLERR_INVALIDPARAMETER:
1141             err = _("a parameter failed to be validated by the DDEML.");
1142             break;
1143         case DMLERR_LOW_MEMORY:
1144             err = _("a DDEML application has created a prolonged race condition.");
1145             break;
1146         case DMLERR_MEMORY_ERROR:
1147             err = _("a memory allocation failed.");
1148             break;
1149         case DMLERR_NO_CONV_ESTABLISHED:
1150             err = _("a client's attempt to establish a conversation has failed.");
1151             break;
1152         case DMLERR_NOTPROCESSED:
1153             err = _("a transaction failed.");
1154             break;
1155         case DMLERR_POKEACKTIMEOUT:
1156             err = _("a request for a synchronous poke transaction has timed out.");
1157             break;
1158         case DMLERR_POSTMSG_FAILED:
1159             err = _("an internal call to the PostMessage function has failed. ");
1160             break;
1161         case DMLERR_REENTRANCY:
1162             err = _("reentrancy problem.");
1163             break;
1164         case DMLERR_SERVER_DIED:
1165             err = _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1166             break;
1167         case DMLERR_SYS_ERROR:
1168             err = _("an internal error has occurred in the DDEML.");
1169             break;
1170         case DMLERR_UNADVACKTIMEOUT:
1171             err = _("a request to end an advise transaction has timed out.");
1172             break;
1173         case DMLERR_UNFOUND_QUEUE_ID:
1174             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.");
1175             break;
1176         default:
1177             err.Printf(_("Unknown DDE error %08x"), error);
1178     }
1179 
1180     return err;
1181 }
1182 
1183 #endif
1184   // wxUSE_IPC
1185