]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/dde.cpp
Add some version checks to help compiling on OSX.
[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_DYNAMIC_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(wxT("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 wxT("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->OnDisconnect(); // May delete the node implicitly
331 node = next;
332 }
333
334 // If any left after this, delete them
335 node = m_connections.GetFirst();
336 while (node)
337 {
338 wxDDEConnection *connection = node->GetData();
339 wxDDEConnectionList::compatibility_iterator next = node->GetNext();
340 delete connection;
341 node = next;
342 }
343}
344
345wxConnectionBase *wxDDEServer::OnAcceptConnection(const wxString& /* topic */)
346{
347 return new wxDDEConnection;
348}
349
350wxDDEConnection *wxDDEServer::FindConnection(WXHCONV conv)
351{
352 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
353 wxDDEConnection *found = NULL;
354 while (node && !found)
355 {
356 wxDDEConnection *connection = node->GetData();
357 if (connection->m_hConv == conv)
358 found = connection;
359 else node = node->GetNext();
360 }
361 return found;
362}
363
364// Only delete the entry in the map, not the actual connection
365bool wxDDEServer::DeleteConnection(WXHCONV conv)
366{
367 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
368 while (node)
369 {
370 wxDDEConnection *connection = node->GetData();
371 if (connection->m_hConv == conv)
372 {
373 m_connections.Erase(node);
374 return true;
375 }
376 else
377 {
378 node = node->GetNext();
379 }
380 }
381 return false;
382}
383
384// ----------------------------------------------------------------------------
385// wxDDEClient
386// ----------------------------------------------------------------------------
387
388wxDDEClient::wxDDEClient()
389{
390 wxDDEInitialize();
391
392 wxDDEClientObjects.Append(this);
393}
394
395wxDDEClient::~wxDDEClient()
396{
397 wxDDEClientObjects.DeleteObject(this);
398 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
399 while (node)
400 {
401 wxDDEConnection *connection = node->GetData();
402 delete connection; // Deletes the node implicitly (see ~wxDDEConnection)
403 node = m_connections.GetFirst();
404 }
405}
406
407bool wxDDEClient::ValidHost(const wxString& /* host */)
408{
409 return true;
410}
411
412wxConnectionBase *wxDDEClient::MakeConnection(const wxString& WXUNUSED(host),
413 const wxString& server,
414 const wxString& topic)
415{
416 HSZ hszServer = DDEAtomFromString(server);
417
418 if ( !hszServer )
419 {
420 return NULL;
421 }
422
423
424 HSZ hszTopic = DDEAtomFromString(topic);
425
426 if ( !hszTopic )
427 {
428 DDEFreeString(hszServer);
429 return NULL;
430 }
431
432
433 HCONV hConv = ::DdeConnect(DDEIdInst, hszServer, hszTopic,
434 (PCONVCONTEXT) NULL);
435
436 DDEFreeString(hszServer);
437 DDEFreeString(hszTopic);
438
439
440 if ( !hConv )
441 {
442 DDELogError( wxString::Format(
443 _("Failed to create connection to server '%s' on topic '%s'"),
444 server.c_str(), topic.c_str()) );
445 }
446 else
447 {
448 wxDDEConnection *connection = (wxDDEConnection*) OnMakeConnection();
449 if (connection)
450 {
451 connection->m_hConv = (WXHCONV) hConv;
452 connection->m_topicName = topic;
453 connection->m_client = this;
454 m_connections.Append(connection);
455 return connection;
456 }
457 }
458
459 return NULL;
460}
461
462wxConnectionBase *wxDDEClient::OnMakeConnection()
463{
464 return new wxDDEConnection;
465}
466
467wxDDEConnection *wxDDEClient::FindConnection(WXHCONV conv)
468{
469 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
470 wxDDEConnection *found = NULL;
471 while (node && !found)
472 {
473 wxDDEConnection *connection = node->GetData();
474 if (connection->m_hConv == conv)
475 found = connection;
476 else node = node->GetNext();
477 }
478 return found;
479}
480
481// Only delete the entry in the map, not the actual connection
482bool wxDDEClient::DeleteConnection(WXHCONV conv)
483{
484 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
485 while (node)
486 {
487 wxDDEConnection *connection = node->GetData();
488 if (connection->m_hConv == conv)
489 {
490 m_connections.Erase(node);
491 return true;
492 }
493 else node = node->GetNext();
494 }
495 return false;
496}
497
498// ----------------------------------------------------------------------------
499// wxDDEConnection
500// ----------------------------------------------------------------------------
501
502wxDDEConnection::wxDDEConnection(void *buffer, size_t size)
503 : wxConnectionBase(buffer, size)
504{
505 m_client = NULL;
506 m_server = NULL;
507
508 m_hConv = 0;
509 m_sendingData = NULL;
510}
511
512wxDDEConnection::wxDDEConnection()
513 : wxConnectionBase()
514{
515 m_hConv = 0;
516 m_sendingData = NULL;
517 m_server = NULL;
518 m_client = NULL;
519}
520
521wxDDEConnection::~wxDDEConnection()
522{
523 Disconnect();
524 if (m_server)
525 m_server->GetConnections().DeleteObject(this);
526 else
527 m_client->GetConnections().DeleteObject(this);
528}
529
530// Calls that CLIENT can make
531bool wxDDEConnection::Disconnect()
532{
533 if ( !GetConnected() )
534 return true;
535
536 DDEDeleteConnection(GetHConv());
537
538 bool ok = DdeDisconnect(GetHConv()) != 0;
539 if ( !ok )
540 {
541 DDELogError(wxT("Failed to disconnect from DDE server gracefully"));
542 }
543
544 SetConnected( false ); // so we don't try and disconnect again
545
546 return ok;
547}
548
549bool
550wxDDEConnection::DoExecute(const void *data, size_t size, wxIPCFormat format)
551{
552 wxCHECK_MSG( format == wxIPC_TEXT ||
553 format == wxIPC_UTF8TEXT ||
554 format == wxIPC_UNICODETEXT,
555 false,
556 wxT("wxDDEServer::Execute() supports only text data") );
557
558 wxMemoryBuffer buffer;
559 LPBYTE realData = NULL;
560 size_t realSize = 0;
561 wxMBConv *conv = NULL;
562
563 // Windows only supports either ANSI or UTF-16 format depending on the
564 // build, so we need to convert the data if it doesn't use it already
565#if wxUSE_UNICODE
566 if ( format == wxIPC_TEXT )
567 {
568 conv = &wxConvLibc;
569 }
570 else if ( format == wxIPC_UTF8TEXT )
571 {
572 conv = &wxConvUTF8;
573 }
574 else // no conversion necessary for wxIPC_UNICODETEXT
575 {
576 realData = (LPBYTE)data;
577 realSize = size;
578 }
579
580 if ( conv )
581 {
582 const char * const text = (const char *)data;
583 const size_t len = size/sizeof(char);
584
585 realSize = conv->ToWChar(NULL, 0, text, len);
586 if ( realSize == wxCONV_FAILED )
587 return false;
588
589 realData = (LPBYTE)buffer.GetWriteBuf(realSize*sizeof(wchar_t));
590 if ( !realData )
591 return false;
592
593 realSize = conv->ToWChar((wchar_t *)realData, realSize, text, len);
594 if ( realSize == wxCONV_FAILED )
595 return false;
596 }
597#else // !wxUSE_UNICODE
598 if ( format == wxIPC_UNICODETEXT )
599 {
600 conv = &wxConvLibc;
601 }
602 else if ( format == wxIPC_UTF8TEXT )
603 {
604 // we could implement this in theory but it's not obvious how to pass
605 // the format information and, basically, why bother -- just use
606 // Unicode build
607 wxFAIL_MSG( wxT("UTF-8 text not supported in ANSI build") );
608
609 return false;
610 }
611 else // don't convert wxIPC_TEXT
612 {
613 realData = (LPBYTE)data;
614 realSize = size;
615 }
616
617 if ( conv )
618 {
619 const wchar_t * const wtext = (const wchar_t *)data;
620 const size_t len = size/sizeof(wchar_t);
621
622 realSize = conv->FromWChar(NULL, 0, wtext, len);
623 if ( realSize == wxCONV_FAILED )
624 return false;
625
626 realData = (LPBYTE)buffer.GetWriteBuf(realSize*sizeof(char));
627 if ( !realData )
628 return false;
629
630 realSize = conv->FromWChar((char*)realData, realSize, wtext, len);
631 if ( realSize == wxCONV_FAILED )
632 return false;
633 }
634#endif // wxUSE_UNICODE/!wxUSE_UNICODE
635
636 DWORD result;
637 bool ok = DdeClientTransaction(realData,
638 realSize*sizeof(wxChar),
639 GetHConv(),
640 NULL,
641 // MSDN: if the transaction specified by
642 // the wType parameter does not pass data
643 // or is XTYP_EXECUTE, wFmt should be zero.
644 0,
645 XTYP_EXECUTE,
646 DDE_TIMEOUT,
647 &result) != 0;
648
649 if ( !ok )
650 {
651 DDELogError(wxT("DDE execute request failed"));
652 }
653
654 return ok;
655}
656
657const void *wxDDEConnection::Request(const wxString& item, size_t *size, wxIPCFormat format)
658{
659 DWORD result;
660
661 HSZ atom = DDEGetAtom(item);
662
663 HDDEDATA returned_data = DdeClientTransaction(NULL, 0,
664 GetHConv(),
665 atom, format,
666 XTYP_REQUEST,
667 DDE_TIMEOUT,
668 &result);
669 if ( !returned_data )
670 {
671 DDELogError(wxT("DDE data request failed"));
672
673 return NULL;
674 }
675
676 DWORD len = DdeGetData(returned_data, NULL, 0, 0);
677
678 void *data = GetBufferAtLeast(len);
679 wxASSERT_MSG(data != NULL,
680 wxT("Buffer too small in wxDDEConnection::Request") );
681 (void) DdeGetData(returned_data, (LPBYTE)data, len, 0);
682
683 (void) DdeFreeDataHandle(returned_data);
684
685 if (size)
686 *size = (size_t)len;
687
688 return data;
689}
690
691bool wxDDEConnection::DoPoke(const wxString& item, const void *data, size_t size, wxIPCFormat format)
692{
693 DWORD result;
694
695 HSZ item_atom = DDEGetAtom(item);
696 bool ok = DdeClientTransaction((LPBYTE)data,
697 size,
698 GetHConv(),
699 item_atom, format,
700 XTYP_POKE,
701 DDE_TIMEOUT,
702 &result) != 0;
703 if ( !ok )
704 {
705 DDELogError(_("DDE poke request failed"));
706 }
707
708 return ok;
709}
710
711bool wxDDEConnection::StartAdvise(const wxString& item)
712{
713 DWORD result;
714 HSZ atom = DDEGetAtom(item);
715
716 bool ok = DdeClientTransaction(NULL, 0,
717 GetHConv(),
718 atom, CF_TEXT,
719 XTYP_ADVSTART,
720 DDE_TIMEOUT,
721 &result) != 0;
722 if ( !ok )
723 {
724 DDELogError(_("Failed to establish an advise loop with DDE server"));
725 }
726
727 return ok;
728}
729
730bool wxDDEConnection::StopAdvise(const wxString& item)
731{
732 DWORD result;
733 HSZ atom = DDEGetAtom(item);
734
735 bool ok = DdeClientTransaction(NULL, 0,
736 GetHConv(),
737 atom, CF_TEXT,
738 XTYP_ADVSTOP,
739 DDE_TIMEOUT,
740 &result) != 0;
741 if ( !ok )
742 {
743 DDELogError(_("Failed to terminate the advise loop with DDE server"));
744 }
745
746 return ok;
747}
748
749// Calls that SERVER can make
750bool wxDDEConnection::DoAdvise(const wxString& item,
751 const void *data,
752 size_t size,
753 wxIPCFormat format)
754{
755 HSZ item_atom = DDEGetAtom(item);
756 HSZ topic_atom = DDEGetAtom(m_topicName);
757 m_sendingData = data; // mrf: potential for scope problems here?
758 m_dataSize = size;
759 // wxIPC_PRIVATE does not succeed, so use text instead
760 m_dataType = format == wxIPC_PRIVATE ? wxIPC_TEXT : format;
761
762 bool ok = DdePostAdvise(DDEIdInst, topic_atom, item_atom) != 0;
763 if ( !ok )
764 {
765 DDELogError(_("Failed to send DDE advise notification"));
766 }
767
768 return ok;
769}
770
771// ----------------------------------------------------------------------------
772// _DDECallback
773// ----------------------------------------------------------------------------
774
775#define DDERETURN HDDEDATA
776
777HDDEDATA EXPENTRY _EXPORT
778_DDECallback(WORD wType,
779 WORD wFmt,
780 HCONV hConv,
781 HSZ hsz1,
782 HSZ hsz2,
783 HDDEDATA hData,
784 DWORD WXUNUSED(lData1),
785 DWORD WXUNUSED(lData2))
786{
787 switch (wType)
788 {
789 case XTYP_CONNECT:
790 {
791 wxString topic = DDEStringFromAtom(hsz1),
792 srv = DDEStringFromAtom(hsz2);
793 wxDDEServer *server = DDEFindServer(srv);
794 if (server)
795 {
796 wxDDEConnection *connection =
797 (wxDDEConnection*) server->OnAcceptConnection(topic);
798 if (connection)
799 {
800 connection->m_server = server;
801 server->GetConnections().Append(connection);
802 connection->m_hConv = 0;
803 connection->m_topicName = topic;
804 DDECurrentlyConnecting = connection;
805 return (DDERETURN)(DWORD)true;
806 }
807 }
808 break;
809 }
810
811 case XTYP_CONNECT_CONFIRM:
812 {
813 if (DDECurrentlyConnecting)
814 {
815 DDECurrentlyConnecting->m_hConv = (WXHCONV) hConv;
816 DDECurrentlyConnecting = NULL;
817 return (DDERETURN)(DWORD)true;
818 }
819 break;
820 }
821
822 case XTYP_DISCONNECT:
823 {
824 wxDDEConnection *connection = DDEFindConnection(hConv);
825 if (connection)
826 {
827 connection->SetConnected( false );
828 if (connection->OnDisconnect())
829 {
830 DDEDeleteConnection(hConv); // Delete mapping: hConv => connection
831 return (DDERETURN)(DWORD)true;
832 }
833 }
834 break;
835 }
836
837 case XTYP_EXECUTE:
838 {
839 wxDDEConnection *connection = DDEFindConnection(hConv);
840
841 if (connection)
842 {
843 DWORD len = DdeGetData(hData, NULL, 0, 0);
844
845 void *data = connection->GetBufferAtLeast(len);
846 wxASSERT_MSG(data != NULL,
847 wxT("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
848
849 DdeGetData(hData, (LPBYTE)data, len, 0);
850
851 DdeFreeDataHandle(hData);
852
853 // XTYP_EXECUTE can be used for text only and the text is
854 // always in ANSI format for ANSI build and Unicode format
855 // in Unicode build
856 #if wxUSE_UNICODE
857 wFmt = wxIPC_UNICODETEXT;
858 #else
859 wFmt = wxIPC_TEXT;
860 #endif
861
862 if ( connection->OnExecute(connection->m_topicName,
863 data,
864 (int)len,
865 (wxIPCFormat)wFmt) )
866 {
867 return (DDERETURN)(DWORD)DDE_FACK;
868 }
869 }
870
871 return (DDERETURN)DDE_FNOTPROCESSED;
872 }
873
874 case XTYP_REQUEST:
875 {
876 wxDDEConnection *connection = DDEFindConnection(hConv);
877
878 if (connection)
879 {
880 wxString item_name = DDEStringFromAtom(hsz2);
881
882 size_t user_size = wxNO_LEN;
883 const void *data = connection->OnRequest(connection->m_topicName,
884 item_name,
885 &user_size,
886 (wxIPCFormat)wFmt);
887 if (data)
888 {
889 if (user_size == wxNO_LEN)
890 switch (wFmt)
891 {
892 case wxIPC_TEXT:
893 case wxIPC_UTF8TEXT:
894 user_size = strlen((const char*)data) + 1; // includes final NUL
895 break;
896 case wxIPC_UNICODETEXT:
897 user_size = (wcslen((const wchar_t*)data) + 1) * sizeof(wchar_t); // includes final NUL
898 break;
899 default:
900 user_size = 0;
901 }
902
903 HDDEDATA handle = DdeCreateDataHandle(DDEIdInst,
904 (LPBYTE)data,
905 user_size,
906 0,
907 hsz2,
908 wFmt,
909 0);
910 return (DDERETURN)handle;
911 }
912 }
913 break;
914 }
915
916 case XTYP_POKE:
917 {
918 wxDDEConnection *connection = DDEFindConnection(hConv);
919
920 if (connection)
921 {
922 wxString item_name = DDEStringFromAtom(hsz2);
923
924 DWORD len = DdeGetData(hData, NULL, 0, 0);
925
926 void *data = connection->GetBufferAtLeast(len);
927 wxASSERT_MSG(data != NULL,
928 wxT("Buffer too small in _DDECallback (XTYP_POKE)") );
929
930 DdeGetData(hData, (LPBYTE)data, len, 0);
931
932 DdeFreeDataHandle(hData);
933
934 connection->OnPoke(connection->m_topicName,
935 item_name,
936 data,
937 (int)len,
938 (wxIPCFormat) wFmt);
939
940 return (DDERETURN)DDE_FACK;
941 }
942 else
943 {
944 return (DDERETURN)DDE_FNOTPROCESSED;
945 }
946 }
947
948 case XTYP_ADVSTART:
949 {
950 wxDDEConnection *connection = DDEFindConnection(hConv);
951
952 if (connection)
953 {
954 wxString item_name = DDEStringFromAtom(hsz2);
955
956 return (DDERETURN)connection->
957 OnStartAdvise(connection->m_topicName, item_name);
958 }
959
960 break;
961 }
962
963 case XTYP_ADVSTOP:
964 {
965 wxDDEConnection *connection = DDEFindConnection(hConv);
966
967 if (connection)
968 {
969 wxString item_name = DDEStringFromAtom(hsz2);
970
971 return (DDERETURN)connection->
972 OnStopAdvise(connection->m_topicName, item_name);
973 }
974
975 break;
976 }
977
978 case XTYP_ADVREQ:
979 {
980 wxDDEConnection *connection = DDEFindConnection(hConv);
981
982 if (connection && connection->m_sendingData)
983 {
984 HDDEDATA data = DdeCreateDataHandle
985 (
986 DDEIdInst,
987 (LPBYTE)connection->m_sendingData,
988 connection->m_dataSize,
989 0,
990 hsz2,
991 connection->m_dataType,
992 0
993 );
994
995 connection->m_sendingData = NULL;
996
997 return (DDERETURN)data;
998 }
999
1000 break;
1001 }
1002
1003 case XTYP_ADVDATA:
1004 {
1005 wxDDEConnection *connection = DDEFindConnection(hConv);
1006
1007 if (connection)
1008 {
1009 wxString item_name = DDEStringFromAtom(hsz2);
1010
1011 DWORD len = DdeGetData(hData, NULL, 0, 0);
1012
1013 void *data = connection->GetBufferAtLeast(len);
1014 wxASSERT_MSG(data != NULL,
1015 wxT("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
1016
1017 DdeGetData(hData, (LPBYTE)data, len, 0);
1018
1019 DdeFreeDataHandle(hData);
1020 if ( connection->OnAdvise(connection->m_topicName,
1021 item_name,
1022 data,
1023 (int)len,
1024 (wxIPCFormat) wFmt) )
1025 {
1026 return (DDERETURN)(DWORD)DDE_FACK;
1027 }
1028 }
1029
1030 return (DDERETURN)DDE_FNOTPROCESSED;
1031 }
1032 }
1033
1034 return (DDERETURN)0;
1035}
1036
1037// ----------------------------------------------------------------------------
1038// DDE strings and atoms
1039// ----------------------------------------------------------------------------
1040
1041// Atom table stuff
1042static HSZ DDEAddAtom(const wxString& str)
1043{
1044 HSZ atom = DDEAtomFromString(str);
1045 wxAtomTable[str] = atom;
1046 return atom;
1047}
1048
1049static HSZ DDEGetAtom(const wxString& str)
1050{
1051 wxAtomMap::iterator it = wxAtomTable.find(str);
1052
1053 if (it != wxAtomTable.end())
1054 return it->second;
1055
1056 return DDEAddAtom(str);
1057}
1058
1059/* atom <-> strings
1060The returned handle has to be freed by the caller (using
1061(static) DDEFreeString).
1062*/
1063static HSZ DDEAtomFromString(const wxString& s)
1064{
1065 wxASSERT_MSG( DDEIdInst, wxT("DDE not initialized") );
1066
1067 HSZ hsz = DdeCreateStringHandle(DDEIdInst, (wxChar*)s.wx_str(), DDE_CP);
1068 if ( !hsz )
1069 {
1070 DDELogError(_("Failed to create DDE string"));
1071 }
1072
1073 return hsz;
1074}
1075
1076static wxString DDEStringFromAtom(HSZ hsz)
1077{
1078 // all DDE strings are normally limited to 255 bytes
1079 static const size_t len = 256;
1080
1081 wxString s;
1082 (void)DdeQueryString(DDEIdInst, hsz, wxStringBuffer(s, len), len, DDE_CP);
1083
1084 return s;
1085}
1086
1087static void DDEFreeString(HSZ hsz)
1088{
1089 // DS: Failure to free a string handle might indicate there's
1090 // some other severe error.
1091 bool ok = (::DdeFreeStringHandle(DDEIdInst, hsz) != 0);
1092 wxASSERT_MSG( ok, wxT("Failed to free DDE string handle") );
1093 wxUnusedVar(ok);
1094}
1095
1096// ----------------------------------------------------------------------------
1097// error handling
1098// ----------------------------------------------------------------------------
1099
1100static void DDELogError(const wxString& s, UINT error)
1101{
1102 if ( !error )
1103 {
1104 error = DdeGetLastError(DDEIdInst);
1105 }
1106
1107 wxLogError(s + wxT(": ") + DDEGetErrorMsg(error));
1108}
1109
1110static wxString DDEGetErrorMsg(UINT error)
1111{
1112 wxString err;
1113 switch ( error )
1114 {
1115 case DMLERR_NO_ERROR:
1116 err = _("no DDE error.");
1117 break;
1118
1119 case DMLERR_ADVACKTIMEOUT:
1120 err = _("a request for a synchronous advise transaction has timed out.");
1121 break;
1122 case DMLERR_BUSY:
1123 err = _("the response to the transaction caused the DDE_FBUSY bit to be set.");
1124 break;
1125 case DMLERR_DATAACKTIMEOUT:
1126 err = _("a request for a synchronous data transaction has timed out.");
1127 break;
1128 case DMLERR_DLL_NOT_INITIALIZED:
1129 err = _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
1130 break;
1131 case DMLERR_DLL_USAGE:
1132 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.");
1133 break;
1134 case DMLERR_EXECACKTIMEOUT:
1135 err = _("a request for a synchronous execute transaction has timed out.");
1136 break;
1137 case DMLERR_INVALIDPARAMETER:
1138 err = _("a parameter failed to be validated by the DDEML.");
1139 break;
1140 case DMLERR_LOW_MEMORY:
1141 err = _("a DDEML application has created a prolonged race condition.");
1142 break;
1143 case DMLERR_MEMORY_ERROR:
1144 err = _("a memory allocation failed.");
1145 break;
1146 case DMLERR_NO_CONV_ESTABLISHED:
1147 err = _("a client's attempt to establish a conversation has failed.");
1148 break;
1149 case DMLERR_NOTPROCESSED:
1150 err = _("a transaction failed.");
1151 break;
1152 case DMLERR_POKEACKTIMEOUT:
1153 err = _("a request for a synchronous poke transaction has timed out.");
1154 break;
1155 case DMLERR_POSTMSG_FAILED:
1156 err = _("an internal call to the PostMessage function has failed. ");
1157 break;
1158 case DMLERR_REENTRANCY:
1159 err = _("reentrancy problem.");
1160 break;
1161 case DMLERR_SERVER_DIED:
1162 err = _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
1163 break;
1164 case DMLERR_SYS_ERROR:
1165 err = _("an internal error has occurred in the DDEML.");
1166 break;
1167 case DMLERR_UNADVACKTIMEOUT:
1168 err = _("a request to end an advise transaction has timed out.");
1169 break;
1170 case DMLERR_UNFOUND_QUEUE_ID:
1171 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.");
1172 break;
1173 default:
1174 err.Printf(_("Unknown DDE error %08x"), error);
1175 }
1176
1177 return err;
1178}
1179
1180#endif
1181 // wxUSE_IPC