]> git.saurik.com Git - wxWidgets.git/blame - src/msw/dde.cpp
fix off by 1 error in GetTextRaw() (#4317)
[wxWidgets.git] / src / msw / dde.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
df69528b 2// Name: src/msw/dde.cpp
2bda0e17
KB
3// Purpose: DDE classes
4// Author: Julian Smart
5// Modified by:
6// Created: 01/02/97
7// RCS-ID: $Id$
6c9a19aa 8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
5bd3a2da
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
2bda0e17
KB
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
5bd3a2da 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
47d67540 27#if wxUSE_IPC
2bda0e17
KB
28
29#ifndef WX_PRECOMP
5bd3a2da
VZ
30 #include "wx/utils.h"
31 #include "wx/app.h"
df69528b 32 #include "wx/hashmap.h"
02761f6c 33 #include "wx/module.h"
2bda0e17
KB
34#endif
35
2bda0e17 36#include "wx/dde.h"
56880523 37#include "wx/intl.h"
8b1350d4
VZ
38#include "wx/buffer.h"
39#include "wx/strconv.h"
2bda0e17 40
03ab016d 41#include "wx/msw/private.h"
5bd3a2da
VZ
42
43#include <string.h>
03ab016d
JS
44#include <ddeml.h>
45
5bd3a2da
VZ
46// ----------------------------------------------------------------------------
47// macros and constants
48// ----------------------------------------------------------------------------
2bda0e17
KB
49
50#ifdef __WIN32__
5bd3a2da 51 #define _EXPORT
2bda0e17 52#else
5bd3a2da 53 #define _EXPORT _export
2bda0e17
KB
54#endif
55
5bd3a2da
VZ
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// ----------------------------------------------------------------------------
2bda0e17
KB
70
71static wxDDEConnection *DDEFindConnection(HCONV hConv);
72static void DDEDeleteConnection(HCONV hConv);
73static wxDDEServer *DDEFindServer(const wxString& s);
74
5bd3a2da
VZ
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);
2bda0e17
KB
83
84// Add topic name to atom table before using in conversations
85static HSZ DDEAddAtom(const wxString& string);
86static HSZ DDEGetAtom(const wxString& string);
5bd3a2da
VZ
87
88// string handles
89static HSZ DDEAtomFromString(const wxString& s);
90static wxString DDEStringFromAtom(HSZ hsz);
0ebb0e5c 91static void DDEFreeString(HSZ hsz);
5bd3a2da
VZ
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// ----------------------------------------------------------------------------
2bda0e17 100
3f5c62f9 101WX_DECLARE_STRING_HASH_MAP( HSZ, wxAtomMap );
df5168c4 102
2bda0e17
KB
103static DWORD DDEIdInst = 0L;
104static wxDDEConnection *DDECurrentlyConnecting = NULL;
df5168c4 105static wxAtomMap wxAtomTable;
d162a7ee
VZ
106
107#include "wx/listimpl.cpp"
108
4115960d
VZ
109WX_DEFINE_LIST(wxDDEClientList)
110WX_DEFINE_LIST(wxDDEServerList)
111WX_DEFINE_LIST(wxDDEConnectionList)
d162a7ee
VZ
112
113static wxDDEClientList wxDDEClientObjects;
114static wxDDEServerList wxDDEServerObjects;
2bda0e17 115
cdd64cc0 116static bool DDEInitialized = false;
2bda0e17 117
5bd3a2da
VZ
118// ----------------------------------------------------------------------------
119// private classes
120// ----------------------------------------------------------------------------
2bda0e17 121
5bd3a2da
VZ
122// A module to allow DDE cleanup without calling these functions
123// from app.cpp or from the user's application.
2bda0e17 124
5bd3a2da 125class wxDDEModule : public wxModule
2bda0e17 126{
5bd3a2da
VZ
127public:
128 wxDDEModule() {}
cdd64cc0 129 bool OnInit() { return true; }
5bd3a2da 130 void OnExit() { wxDDECleanUp(); }
2bda0e17 131
5bd3a2da
VZ
132private:
133 DECLARE_DYNAMIC_CLASS(wxDDEModule)
134};
2bda0e17 135
5bd3a2da
VZ
136// ----------------------------------------------------------------------------
137// wxWin macros
138// ----------------------------------------------------------------------------
2bda0e17 139
5bd3a2da
VZ
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()
2bda0e17 154{
5bd3a2da
VZ
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 {
cdd64cc0 167 DDEInitialized = true;
5bd3a2da
VZ
168 }
169 }
2bda0e17
KB
170}
171
5bd3a2da 172void wxDDECleanUp()
e2a6f233 173{
bd947cc9
VZ
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") );
5a33bc09 178
df5168c4 179 wxAtomTable.clear();
5a33bc09 180
5bd3a2da
VZ
181 if ( DDEIdInst != 0 )
182 {
183 DdeUninitialize(DDEIdInst);
184 DDEIdInst = 0;
185 }
5bd3a2da
VZ
186}
187
188// ----------------------------------------------------------------------------
189// functions working with the global connection list(s)
190// ----------------------------------------------------------------------------
e2a6f233 191
2bda0e17
KB
192// Global find connection
193static wxDDEConnection *DDEFindConnection(HCONV hConv)
194{
df5168c4 195 wxDDEServerList::compatibility_iterator serverNode = wxDDEServerObjects.GetFirst();
d162a7ee
VZ
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
df5168c4 209 wxDDEClientList::compatibility_iterator clientNode = wxDDEClientObjects.GetFirst();
d162a7ee
VZ
210 while (clientNode && !found)
211 {
212 wxDDEClient *object = clientNode->GetData();
213 found = object->FindConnection((WXHCONV) hConv);
214 clientNode = clientNode->GetNext();
215 }
216 return found;
2bda0e17
KB
217}
218
219// Global delete connection
220static void DDEDeleteConnection(HCONV hConv)
221{
df5168c4 222 wxDDEServerList::compatibility_iterator serverNode = wxDDEServerObjects.GetFirst();
d162a7ee
VZ
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
df5168c4 235 wxDDEClientList::compatibility_iterator clientNode = wxDDEClientObjects.GetFirst();
d162a7ee
VZ
236 while (clientNode && !found)
237 {
238 wxDDEClient *object = clientNode->GetData();
239 found = object->DeleteConnection((WXHCONV) hConv);
240 clientNode = clientNode->GetNext();
241 }
2bda0e17
KB
242}
243
244// Find a server from a service name
245static wxDDEServer *DDEFindServer(const wxString& s)
246{
df5168c4 247 wxDDEServerList::compatibility_iterator node = wxDDEServerObjects.GetFirst();
d162a7ee
VZ
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;
2bda0e17
KB
264}
265
5bd3a2da
VZ
266// ----------------------------------------------------------------------------
267// wxDDEServer
268// ----------------------------------------------------------------------------
2bda0e17 269
5bd3a2da 270wxDDEServer::wxDDEServer()
2bda0e17 271{
5bd3a2da
VZ
272 wxDDEInitialize();
273
274 wxDDEServerObjects.Append(this);
2bda0e17
KB
275}
276
5bd3a2da 277bool wxDDEServer::Create(const wxString& server)
2bda0e17 278{
5bd3a2da 279 m_serviceName = server;
2bda0e17 280
0ebb0e5c 281 HSZ hsz = DDEAtomFromString(server);
5bd3a2da 282
0ebb0e5c
DS
283 if ( !hsz )
284 {
cdd64cc0 285 return false;
5bd3a2da
VZ
286 }
287
0ebb0e5c
DS
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;
2bda0e17
KB
301}
302
5bd3a2da 303wxDDEServer::~wxDDEServer()
2bda0e17 304{
df69528b 305 if ( !m_serviceName.empty() )
2bda0e17 306 {
0ebb0e5c
DS
307 HSZ hsz = DDEAtomFromString(m_serviceName);
308
309 if (hsz)
5bd3a2da 310 {
0ebb0e5c
DS
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);
5bd3a2da 320 }
2bda0e17 321 }
2bda0e17 322
5bd3a2da 323 wxDDEServerObjects.DeleteObject(this);
2bda0e17 324
df5168c4 325 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
5bd3a2da
VZ
326 while (node)
327 {
d162a7ee 328 wxDDEConnection *connection = node->GetData();
df5168c4 329 wxDDEConnectionList::compatibility_iterator next = node->GetNext();
f010ad48 330 connection->SetConnected(false);
5bd3a2da
VZ
331 connection->OnDisconnect(); // May delete the node implicitly
332 node = next;
333 }
334
335 // If any left after this, delete them
cdd64cc0 336 node = m_connections.GetFirst();
5bd3a2da
VZ
337 while (node)
338 {
d162a7ee 339 wxDDEConnection *connection = node->GetData();
df5168c4 340 wxDDEConnectionList::compatibility_iterator next = node->GetNext();
5bd3a2da
VZ
341 delete connection;
342 node = next;
343 }
2bda0e17
KB
344}
345
346wxConnectionBase *wxDDEServer::OnAcceptConnection(const wxString& /* topic */)
347{
5bd3a2da 348 return new wxDDEConnection;
2bda0e17
KB
349}
350
351wxDDEConnection *wxDDEServer::FindConnection(WXHCONV conv)
352{
df5168c4 353 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
5bd3a2da
VZ
354 wxDDEConnection *found = NULL;
355 while (node && !found)
356 {
d162a7ee 357 wxDDEConnection *connection = node->GetData();
5bd3a2da
VZ
358 if (connection->m_hConv == conv)
359 found = connection;
cdd64cc0 360 else node = node->GetNext();
5bd3a2da
VZ
361 }
362 return found;
2bda0e17
KB
363}
364
365// Only delete the entry in the map, not the actual connection
366bool wxDDEServer::DeleteConnection(WXHCONV conv)
367{
df5168c4
MB
368 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
369 while (node)
2bda0e17 370 {
d162a7ee 371 wxDDEConnection *connection = node->GetData();
5bd3a2da
VZ
372 if (connection->m_hConv == conv)
373 {
df5168c4
MB
374 m_connections.Erase(node);
375 return true;
5bd3a2da 376 }
d162a7ee
VZ
377 else
378 {
379 node = node->GetNext();
380 }
2bda0e17 381 }
df5168c4 382 return false;
2bda0e17
KB
383}
384
5bd3a2da
VZ
385// ----------------------------------------------------------------------------
386// wxDDEClient
387// ----------------------------------------------------------------------------
2bda0e17 388
5bd3a2da 389wxDDEClient::wxDDEClient()
2bda0e17 390{
5bd3a2da
VZ
391 wxDDEInitialize();
392
393 wxDDEClientObjects.Append(this);
2bda0e17
KB
394}
395
5bd3a2da 396wxDDEClient::~wxDDEClient()
2bda0e17 397{
5bd3a2da 398 wxDDEClientObjects.DeleteObject(this);
df5168c4 399 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
5bd3a2da
VZ
400 while (node)
401 {
d162a7ee 402 wxDDEConnection *connection = node->GetData();
5bd3a2da 403 delete connection; // Deletes the node implicitly (see ~wxDDEConnection)
cdd64cc0 404 node = m_connections.GetFirst();
5bd3a2da 405 }
2bda0e17
KB
406}
407
408bool wxDDEClient::ValidHost(const wxString& /* host */)
409{
cdd64cc0 410 return true;
2bda0e17
KB
411}
412
5bd3a2da
VZ
413wxConnectionBase *wxDDEClient::MakeConnection(const wxString& WXUNUSED(host),
414 const wxString& server,
415 const wxString& topic)
2bda0e17 416{
0ebb0e5c
DS
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
5bd3a2da 441 if ( !hConv )
2bda0e17 442 {
0ebb0e5c
DS
443 DDELogError( wxString::Format(
444 _("Failed to create connection to server '%s' on topic '%s'"),
445 server.c_str(), topic.c_str()) );
2bda0e17 446 }
5bd3a2da
VZ
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;
2bda0e17
KB
461}
462
5bd3a2da 463wxConnectionBase *wxDDEClient::OnMakeConnection()
2bda0e17 464{
5bd3a2da 465 return new wxDDEConnection;
2bda0e17
KB
466}
467
468wxDDEConnection *wxDDEClient::FindConnection(WXHCONV conv)
469{
df5168c4 470 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
5bd3a2da
VZ
471 wxDDEConnection *found = NULL;
472 while (node && !found)
473 {
d162a7ee 474 wxDDEConnection *connection = node->GetData();
5bd3a2da
VZ
475 if (connection->m_hConv == conv)
476 found = connection;
cdd64cc0 477 else node = node->GetNext();
5bd3a2da
VZ
478 }
479 return found;
2bda0e17
KB
480}
481
482// Only delete the entry in the map, not the actual connection
483bool wxDDEClient::DeleteConnection(WXHCONV conv)
484{
df5168c4
MB
485 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
486 while (node)
2bda0e17 487 {
d162a7ee 488 wxDDEConnection *connection = node->GetData();
5bd3a2da
VZ
489 if (connection->m_hConv == conv)
490 {
df5168c4
MB
491 m_connections.Erase(node);
492 return true;
5bd3a2da 493 }
cdd64cc0 494 else node = node->GetNext();
2bda0e17 495 }
df5168c4 496 return false;
2bda0e17
KB
497}
498
5bd3a2da
VZ
499// ----------------------------------------------------------------------------
500// wxDDEConnection
501// ----------------------------------------------------------------------------
2bda0e17 502
50c549b9 503wxDDEConnection::wxDDEConnection(void *buffer, size_t size)
f010ad48 504 : wxConnectionBase(buffer, size)
2bda0e17 505{
5bd3a2da
VZ
506 m_client = NULL;
507 m_server = NULL;
2bda0e17 508
5bd3a2da
VZ
509 m_hConv = 0;
510 m_sendingData = NULL;
2bda0e17
KB
511}
512
5bd3a2da 513wxDDEConnection::wxDDEConnection()
f010ad48 514 : wxConnectionBase()
2bda0e17 515{
5bd3a2da
VZ
516 m_hConv = 0;
517 m_sendingData = NULL;
518 m_server = NULL;
519 m_client = NULL;
2bda0e17
KB
520}
521
5bd3a2da 522wxDDEConnection::~wxDDEConnection()
2bda0e17 523{
f010ad48 524 Disconnect();
5bd3a2da
VZ
525 if (m_server)
526 m_server->GetConnections().DeleteObject(this);
527 else
528 m_client->GetConnections().DeleteObject(this);
2bda0e17
KB
529}
530
531// Calls that CLIENT can make
5bd3a2da 532bool wxDDEConnection::Disconnect()
2bda0e17 533{
f010ad48
JS
534 if ( !GetConnected() )
535 return true;
536
5bd3a2da
VZ
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
f010ad48
JS
545 SetConnected( false ); // so we don't try and disconnect again
546
5bd3a2da 547 return ok;
2bda0e17
KB
548}
549
8b1350d4
VZ
550bool
551wxDDEConnection::DoExecute(const void *data, size_t size, wxIPCFormat format)
2bda0e17 552{
8b1350d4
VZ
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;
d6906a06
VZ
560 LPBYTE realData = NULL;
561 size_t realSize = 0;
8b1350d4
VZ
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 }
2bda0e17 580
8b1350d4
VZ
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,
b81885a6
VZ
639 realSize*sizeof(wxChar),
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;
6ba63600 649
5bd3a2da
VZ
650 if ( !ok )
651 {
652 DDELogError(_T("DDE execute request failed"));
653 }
2bda0e17 654
5bd3a2da 655 return ok;
2bda0e17
KB
656}
657
50c549b9 658const void *wxDDEConnection::Request(const wxString& item, size_t *size, wxIPCFormat format)
2bda0e17 659{
5bd3a2da 660 DWORD result;
f010ad48 661
5bd3a2da
VZ
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 }
2bda0e17 676
f010ad48
JS
677 DWORD len = DdeGetData(returned_data, NULL, 0, 0);
678
50c549b9 679 void *data = GetBufferAtLeast(len);
f010ad48
JS
680 wxASSERT_MSG(data != NULL,
681 _T("Buffer too small in wxDDEConnection::Request") );
0ebb0e5c 682 (void) DdeGetData(returned_data, (LPBYTE)data, len, 0);
2bda0e17 683
0ebb0e5c 684 (void) DdeFreeDataHandle(returned_data);
2bda0e17 685
5bd3a2da 686 if (size)
50c549b9 687 *size = (size_t)len;
2bda0e17 688
f010ad48 689 return data;
2bda0e17
KB
690}
691
50c549b9 692bool wxDDEConnection::DoPoke(const wxString& item, const void *data, size_t size, wxIPCFormat format)
2bda0e17 693{
5bd3a2da 694 DWORD result;
2bda0e17 695
5bd3a2da 696 HSZ item_atom = DDEGetAtom(item);
d71cc120 697 bool ok = DdeClientTransaction((LPBYTE)data,
9d860992 698 size,
5bd3a2da
VZ
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 }
2bda0e17 708
5bd3a2da 709 return ok;
2bda0e17
KB
710}
711
712bool wxDDEConnection::StartAdvise(const wxString& item)
713{
5bd3a2da
VZ
714 DWORD result;
715 HSZ atom = DDEGetAtom(item);
2bda0e17 716
5bd3a2da
VZ
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;
2bda0e17
KB
729}
730
731bool wxDDEConnection::StopAdvise(const wxString& item)
732{
5bd3a2da
VZ
733 DWORD result;
734 HSZ atom = DDEGetAtom(item);
2bda0e17 735
5bd3a2da
VZ
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;
2bda0e17
KB
748}
749
750// Calls that SERVER can make
50c549b9
VZ
751bool wxDDEConnection::DoAdvise(const wxString& item,
752 const void *data,
753 size_t size,
754 wxIPCFormat format)
2bda0e17 755{
5bd3a2da
VZ
756 HSZ item_atom = DDEGetAtom(item);
757 HSZ topic_atom = DDEGetAtom(m_topicName);
f010ad48 758 m_sendingData = data; // mrf: potential for scope problems here?
5bd3a2da 759 m_dataSize = size;
9d860992
JS
760 // wxIPC_PRIVATE does not succeed, so use text instead
761 m_dataType = format == wxIPC_PRIVATE ? wxIPC_TEXT : format;
2bda0e17 762
5bd3a2da
VZ
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;
2bda0e17
KB
770}
771
5bd3a2da
VZ
772// ----------------------------------------------------------------------------
773// _DDECallback
774// ----------------------------------------------------------------------------
2bda0e17
KB
775
776#define DDERETURN HDDEDATA
777
5bd3a2da
VZ
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)
2bda0e17 789 {
5bd3a2da
VZ
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;
cdd64cc0 806 return (DDERETURN)(DWORD)true;
5bd3a2da
VZ
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;
cdd64cc0 818 return (DDERETURN)(DWORD)true;
5bd3a2da
VZ
819 }
820 break;
821 }
822
823 case XTYP_DISCONNECT:
824 {
825 wxDDEConnection *connection = DDEFindConnection(hConv);
f010ad48 826 if (connection)
5bd3a2da 827 {
f010ad48
JS
828 connection->SetConnected( false );
829 if (connection->OnDisconnect())
830 {
831 DDEDeleteConnection(hConv); // Delete mapping: hConv => connection
cdd64cc0 832 return (DDERETURN)(DWORD)true;
f010ad48 833 }
5bd3a2da
VZ
834 }
835 break;
836 }
837
838 case XTYP_EXECUTE:
839 {
840 wxDDEConnection *connection = DDEFindConnection(hConv);
841
842 if (connection)
843 {
f010ad48
JS
844 DWORD len = DdeGetData(hData, NULL, 0, 0);
845
50c549b9 846 void *data = connection->GetBufferAtLeast(len);
f010ad48
JS
847 wxASSERT_MSG(data != NULL,
848 _T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
849
850 DdeGetData(hData, (LPBYTE)data, len, 0);
851
5bd3a2da 852 DdeFreeDataHandle(hData);
f010ad48 853
8b1350d4
VZ
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
5bd3a2da 863 if ( connection->OnExecute(connection->m_topicName,
f010ad48 864 data,
940ef730 865 (int)len,
8b1350d4 866 (wxIPCFormat)wFmt) )
5bd3a2da
VZ
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
50c549b9
VZ
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);
5bd3a2da
VZ
888 if (data)
889 {
50c549b9
VZ
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 }
5bd3a2da
VZ
903
904 HDDEDATA handle = DdeCreateDataHandle(DDEIdInst,
905 (LPBYTE)data,
9d860992 906 user_size,
5bd3a2da
VZ
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
f010ad48
JS
925 DWORD len = DdeGetData(hData, NULL, 0, 0);
926
50c549b9 927 void *data = connection->GetBufferAtLeast(len);
f010ad48 928 wxASSERT_MSG(data != NULL,
9d860992 929 _T("Buffer too small in _DDECallback (XTYP_POKE)") );
f010ad48
JS
930
931 DdeGetData(hData, (LPBYTE)data, len, 0);
932
5bd3a2da
VZ
933 DdeFreeDataHandle(hData);
934
935 connection->OnPoke(connection->m_topicName,
936 item_name,
f010ad48 937 data,
940ef730 938 (int)len,
5bd3a2da
VZ
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,
9d860992 989 connection->m_dataSize,
5bd3a2da
VZ
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
f010ad48
JS
1012 DWORD len = DdeGetData(hData, NULL, 0, 0);
1013
50c549b9 1014 void *data = connection->GetBufferAtLeast(len);
f010ad48
JS
1015 wxASSERT_MSG(data != NULL,
1016 _T("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
1017
1018 DdeGetData(hData, (LPBYTE)data, len, 0);
1019
5bd3a2da
VZ
1020 DdeFreeDataHandle(hData);
1021 if ( connection->OnAdvise(connection->m_topicName,
1022 item_name,
f010ad48 1023 data,
940ef730 1024 (int)len,
5bd3a2da
VZ
1025 (wxIPCFormat) wFmt) )
1026 {
1027 return (DDERETURN)(DWORD)DDE_FACK;
1028 }
1029 }
1030
1031 return (DDERETURN)DDE_FNOTPROCESSED;
1032 }
2bda0e17
KB
1033 }
1034
5bd3a2da
VZ
1035 return (DDERETURN)0;
1036}
2bda0e17 1037
5bd3a2da
VZ
1038// ----------------------------------------------------------------------------
1039// DDE strings and atoms
1040// ----------------------------------------------------------------------------
2bda0e17 1041
5bd3a2da 1042// Atom table stuff
df5168c4 1043static HSZ DDEAddAtom(const wxString& str)
5bd3a2da 1044{
df5168c4
MB
1045 HSZ atom = DDEAtomFromString(str);
1046 wxAtomTable[str] = atom;
5bd3a2da
VZ
1047 return atom;
1048}
2bda0e17 1049
df5168c4 1050static HSZ DDEGetAtom(const wxString& str)
5bd3a2da 1051{
df5168c4
MB
1052 wxAtomMap::iterator it = wxAtomTable.find(str);
1053
1054 if (it != wxAtomTable.end())
1055 return it->second;
1056
1057 return DDEAddAtom(str);
5bd3a2da 1058}
2bda0e17 1059
0ebb0e5c
DS
1060/* atom <-> strings
1061The returned handle has to be freed by the caller (using
1062(static) DDEFreeString).
1063*/
5bd3a2da
VZ
1064static HSZ DDEAtomFromString(const wxString& s)
1065{
1066 wxASSERT_MSG( DDEIdInst, _T("DDE not initialized") );
1067
c9f78968 1068 HSZ hsz = DdeCreateStringHandle(DDEIdInst, (wxChar*)s.wx_str(), DDE_CP);
5bd3a2da 1069 if ( !hsz )
2bda0e17 1070 {
5bd3a2da 1071 DDELogError(_("Failed to create DDE string"));
2bda0e17
KB
1072 }
1073
5bd3a2da
VZ
1074 return hsz;
1075}
2bda0e17 1076
5bd3a2da
VZ
1077static wxString DDEStringFromAtom(HSZ hsz)
1078{
1079 // all DDE strings are normally limited to 255 bytes
1080 static const size_t len = 256;
2bda0e17 1081
5bd3a2da 1082 wxString s;
de564874 1083 (void)DdeQueryString(DDEIdInst, hsz, wxStringBuffer(s, len), len, DDE_CP);
2bda0e17 1084
5bd3a2da
VZ
1085 return s;
1086}
2bda0e17 1087
0ebb0e5c
DS
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
5bd3a2da
VZ
1097// ----------------------------------------------------------------------------
1098// error handling
1099// ----------------------------------------------------------------------------
2bda0e17 1100
5bd3a2da
VZ
1101static void DDELogError(const wxString& s, UINT error)
1102{
1103 if ( !error )
2bda0e17 1104 {
5bd3a2da 1105 error = DdeGetLastError(DDEIdInst);
2bda0e17 1106 }
2bda0e17 1107
5bd3a2da 1108 wxLogError(s + _T(": ") + DDEGetErrorMsg(error));
2bda0e17
KB
1109}
1110
5bd3a2da 1111static wxString DDEGetErrorMsg(UINT error)
2bda0e17 1112{
5bd3a2da
VZ
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:
f83e3685 1130 err = _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
5bd3a2da
VZ
1131 break;
1132 case DMLERR_DLL_USAGE:
f83e3685 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.");
5bd3a2da
VZ
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:
f83e3685 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.");
5bd3a2da
VZ
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:
f83e3685 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.");
5bd3a2da
VZ
1173 break;
1174 default:
1175 err.Printf(_("Unknown DDE error %08x"), error);
1176 }
2bda0e17 1177
5bd3a2da 1178 return err;
2bda0e17
KB
1179}
1180
1181#endif
47d67540 1182 // wxUSE_IPC