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