]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dde.cpp
cleanup, adding correct window-ids to window events (important when using eg Connect)
[wxWidgets.git] / src / msw / dde.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/dde.cpp
3 // Purpose: DDE classes
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
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 #endif
33
34 #include "wx/module.h"
35 #include "wx/dde.h"
36 #include "wx/intl.h"
37 #include "wx/hashmap.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:
126 wxDDEModule() {}
127 bool OnInit() { return true; }
128 void OnExit() { wxDDECleanUp(); }
129
130 private:
131 DECLARE_DYNAMIC_CLASS(wxDDEModule)
132 };
133
134 // ----------------------------------------------------------------------------
135 // wxWin macros
136 // ----------------------------------------------------------------------------
137
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
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
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
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
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
268 wxDDEServer::wxDDEServer()
269 {
270 wxDDEInitialize();
271
272 wxDDEServerObjects.Append(this);
273 }
274
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
301 wxDDEServer::~wxDDEServer()
302 {
303 if ( !m_serviceName.IsEmpty() )
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
344 wxConnectionBase *wxDDEServer::OnAcceptConnection(const wxString& /* topic */)
345 {
346 return new wxDDEConnection;
347 }
348
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
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
387 wxDDEClient::wxDDEClient()
388 {
389 wxDDEInitialize();
390
391 wxDDEClientObjects.Append(this);
392 }
393
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
406 bool wxDDEClient::ValidHost(const wxString& /* host */)
407 {
408 return true;
409 }
410
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
461 wxConnectionBase *wxDDEClient::OnMakeConnection()
462 {
463 return new wxDDEConnection;
464 }
465
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
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
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
511 wxDDEConnection::wxDDEConnection()
512 : wxConnectionBase()
513 {
514 m_hConv = 0;
515 m_sendingData = NULL;
516 m_server = NULL;
517 m_client = NULL;
518 }
519
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
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
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
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
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
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
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
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
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
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
956 static HSZ DDEAddAtom(const wxString& str)
957 {
958 HSZ atom = DDEAtomFromString(str);
959 wxAtomTable[str] = atom;
960 return atom;
961 }
962
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 */
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
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
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
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
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