]> git.saurik.com Git - wxWidgets.git/blame - src/msw/dde.cpp
Add wxItemContainer::DetachClientObject() and use it in wxRearrangeList.
[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 {
9a83f860 163 DDELogError(wxT("Failed to initialize DDE"), rc);
5bd3a2da
VZ
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(),
9a83f860 177 wxT("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();
5bd3a2da
VZ
330 connection->OnDisconnect(); // May delete the node implicitly
331 node = next;
332 }
333
334 // If any left after this, delete them
cdd64cc0 335 node = m_connections.GetFirst();
5bd3a2da
VZ
336 while (node)
337 {
d162a7ee 338 wxDDEConnection *connection = node->GetData();
df5168c4 339 wxDDEConnectionList::compatibility_iterator next = node->GetNext();
5bd3a2da
VZ
340 delete connection;
341 node = next;
342 }
2bda0e17
KB
343}
344
345wxConnectionBase *wxDDEServer::OnAcceptConnection(const wxString& /* topic */)
346{
5bd3a2da 347 return new wxDDEConnection;
2bda0e17
KB
348}
349
350wxDDEConnection *wxDDEServer::FindConnection(WXHCONV conv)
351{
df5168c4 352 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
5bd3a2da
VZ
353 wxDDEConnection *found = NULL;
354 while (node && !found)
355 {
d162a7ee 356 wxDDEConnection *connection = node->GetData();
5bd3a2da
VZ
357 if (connection->m_hConv == conv)
358 found = connection;
cdd64cc0 359 else node = node->GetNext();
5bd3a2da
VZ
360 }
361 return found;
2bda0e17
KB
362}
363
364// Only delete the entry in the map, not the actual connection
365bool wxDDEServer::DeleteConnection(WXHCONV conv)
366{
df5168c4
MB
367 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
368 while (node)
2bda0e17 369 {
d162a7ee 370 wxDDEConnection *connection = node->GetData();
5bd3a2da
VZ
371 if (connection->m_hConv == conv)
372 {
df5168c4
MB
373 m_connections.Erase(node);
374 return true;
5bd3a2da 375 }
d162a7ee
VZ
376 else
377 {
378 node = node->GetNext();
379 }
2bda0e17 380 }
df5168c4 381 return false;
2bda0e17
KB
382}
383
5bd3a2da
VZ
384// ----------------------------------------------------------------------------
385// wxDDEClient
386// ----------------------------------------------------------------------------
2bda0e17 387
5bd3a2da 388wxDDEClient::wxDDEClient()
2bda0e17 389{
5bd3a2da
VZ
390 wxDDEInitialize();
391
392 wxDDEClientObjects.Append(this);
2bda0e17
KB
393}
394
5bd3a2da 395wxDDEClient::~wxDDEClient()
2bda0e17 396{
5bd3a2da 397 wxDDEClientObjects.DeleteObject(this);
df5168c4 398 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
5bd3a2da
VZ
399 while (node)
400 {
d162a7ee 401 wxDDEConnection *connection = node->GetData();
5bd3a2da 402 delete connection; // Deletes the node implicitly (see ~wxDDEConnection)
cdd64cc0 403 node = m_connections.GetFirst();
5bd3a2da 404 }
2bda0e17
KB
405}
406
407bool wxDDEClient::ValidHost(const wxString& /* host */)
408{
cdd64cc0 409 return true;
2bda0e17
KB
410}
411
5bd3a2da
VZ
412wxConnectionBase *wxDDEClient::MakeConnection(const wxString& WXUNUSED(host),
413 const wxString& server,
414 const wxString& topic)
2bda0e17 415{
0ebb0e5c
DS
416 HSZ hszServer = DDEAtomFromString(server);
417
418 if ( !hszServer )
419 {
d3b9f782 420 return NULL;
0ebb0e5c
DS
421 }
422
423
424 HSZ hszTopic = DDEAtomFromString(topic);
425
426 if ( !hszTopic )
427 {
428 DDEFreeString(hszServer);
d3b9f782 429 return NULL;
0ebb0e5c
DS
430 }
431
432
433 HCONV hConv = ::DdeConnect(DDEIdInst, hszServer, hszTopic,
434 (PCONVCONTEXT) NULL);
435
436 DDEFreeString(hszServer);
437 DDEFreeString(hszTopic);
438
439
5bd3a2da 440 if ( !hConv )
2bda0e17 441 {
0ebb0e5c
DS
442 DDELogError( wxString::Format(
443 _("Failed to create connection to server '%s' on topic '%s'"),
444 server.c_str(), topic.c_str()) );
2bda0e17 445 }
5bd3a2da
VZ
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
d3b9f782 459 return NULL;
2bda0e17
KB
460}
461
5bd3a2da 462wxConnectionBase *wxDDEClient::OnMakeConnection()
2bda0e17 463{
5bd3a2da 464 return new wxDDEConnection;
2bda0e17
KB
465}
466
467wxDDEConnection *wxDDEClient::FindConnection(WXHCONV conv)
468{
df5168c4 469 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
5bd3a2da
VZ
470 wxDDEConnection *found = NULL;
471 while (node && !found)
472 {
d162a7ee 473 wxDDEConnection *connection = node->GetData();
5bd3a2da
VZ
474 if (connection->m_hConv == conv)
475 found = connection;
cdd64cc0 476 else node = node->GetNext();
5bd3a2da
VZ
477 }
478 return found;
2bda0e17
KB
479}
480
481// Only delete the entry in the map, not the actual connection
482bool wxDDEClient::DeleteConnection(WXHCONV conv)
483{
df5168c4
MB
484 wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
485 while (node)
2bda0e17 486 {
d162a7ee 487 wxDDEConnection *connection = node->GetData();
5bd3a2da
VZ
488 if (connection->m_hConv == conv)
489 {
df5168c4
MB
490 m_connections.Erase(node);
491 return true;
5bd3a2da 492 }
cdd64cc0 493 else node = node->GetNext();
2bda0e17 494 }
df5168c4 495 return false;
2bda0e17
KB
496}
497
5bd3a2da
VZ
498// ----------------------------------------------------------------------------
499// wxDDEConnection
500// ----------------------------------------------------------------------------
2bda0e17 501
50c549b9 502wxDDEConnection::wxDDEConnection(void *buffer, size_t size)
f010ad48 503 : wxConnectionBase(buffer, size)
2bda0e17 504{
5bd3a2da
VZ
505 m_client = NULL;
506 m_server = NULL;
2bda0e17 507
5bd3a2da
VZ
508 m_hConv = 0;
509 m_sendingData = NULL;
2bda0e17
KB
510}
511
5bd3a2da 512wxDDEConnection::wxDDEConnection()
f010ad48 513 : wxConnectionBase()
2bda0e17 514{
5bd3a2da
VZ
515 m_hConv = 0;
516 m_sendingData = NULL;
517 m_server = NULL;
518 m_client = NULL;
2bda0e17
KB
519}
520
5bd3a2da 521wxDDEConnection::~wxDDEConnection()
2bda0e17 522{
f010ad48 523 Disconnect();
5bd3a2da
VZ
524 if (m_server)
525 m_server->GetConnections().DeleteObject(this);
526 else
527 m_client->GetConnections().DeleteObject(this);
2bda0e17
KB
528}
529
530// Calls that CLIENT can make
5bd3a2da 531bool wxDDEConnection::Disconnect()
2bda0e17 532{
f010ad48
JS
533 if ( !GetConnected() )
534 return true;
535
5bd3a2da
VZ
536 DDEDeleteConnection(GetHConv());
537
538 bool ok = DdeDisconnect(GetHConv()) != 0;
539 if ( !ok )
540 {
9a83f860 541 DDELogError(wxT("Failed to disconnect from DDE server gracefully"));
5bd3a2da
VZ
542 }
543
f010ad48
JS
544 SetConnected( false ); // so we don't try and disconnect again
545
5bd3a2da 546 return ok;
2bda0e17
KB
547}
548
8b1350d4
VZ
549bool
550wxDDEConnection::DoExecute(const void *data, size_t size, wxIPCFormat format)
2bda0e17 551{
8b1350d4
VZ
552 wxCHECK_MSG( format == wxIPC_TEXT ||
553 format == wxIPC_UTF8TEXT ||
554 format == wxIPC_UNICODETEXT,
555 false,
9a83f860 556 wxT("wxDDEServer::Execute() supports only text data") );
8b1350d4
VZ
557
558 wxMemoryBuffer buffer;
d6906a06
VZ
559 LPBYTE realData = NULL;
560 size_t realSize = 0;
8b1350d4
VZ
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 }
2bda0e17 579
8b1350d4
VZ
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
9a83f860 607 wxFAIL_MSG( wxT("UTF-8 text not supported in ANSI build") );
8b1350d4
VZ
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,
b81885a6
VZ
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;
6ba63600 648
5bd3a2da
VZ
649 if ( !ok )
650 {
9a83f860 651 DDELogError(wxT("DDE execute request failed"));
5bd3a2da 652 }
2bda0e17 653
5bd3a2da 654 return ok;
2bda0e17
KB
655}
656
50c549b9 657const void *wxDDEConnection::Request(const wxString& item, size_t *size, wxIPCFormat format)
2bda0e17 658{
5bd3a2da 659 DWORD result;
f010ad48 660
5bd3a2da
VZ
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 {
9a83f860 671 DDELogError(wxT("DDE data request failed"));
5bd3a2da
VZ
672
673 return NULL;
674 }
2bda0e17 675
f010ad48
JS
676 DWORD len = DdeGetData(returned_data, NULL, 0, 0);
677
50c549b9 678 void *data = GetBufferAtLeast(len);
f010ad48 679 wxASSERT_MSG(data != NULL,
9a83f860 680 wxT("Buffer too small in wxDDEConnection::Request") );
0ebb0e5c 681 (void) DdeGetData(returned_data, (LPBYTE)data, len, 0);
2bda0e17 682
0ebb0e5c 683 (void) DdeFreeDataHandle(returned_data);
2bda0e17 684
5bd3a2da 685 if (size)
50c549b9 686 *size = (size_t)len;
2bda0e17 687
f010ad48 688 return data;
2bda0e17
KB
689}
690
50c549b9 691bool wxDDEConnection::DoPoke(const wxString& item, const void *data, size_t size, wxIPCFormat format)
2bda0e17 692{
5bd3a2da 693 DWORD result;
2bda0e17 694
5bd3a2da 695 HSZ item_atom = DDEGetAtom(item);
d71cc120 696 bool ok = DdeClientTransaction((LPBYTE)data,
9d860992 697 size,
5bd3a2da
VZ
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 }
2bda0e17 707
5bd3a2da 708 return ok;
2bda0e17
KB
709}
710
711bool wxDDEConnection::StartAdvise(const wxString& item)
712{
5bd3a2da
VZ
713 DWORD result;
714 HSZ atom = DDEGetAtom(item);
2bda0e17 715
5bd3a2da
VZ
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;
2bda0e17
KB
728}
729
730bool wxDDEConnection::StopAdvise(const wxString& item)
731{
5bd3a2da
VZ
732 DWORD result;
733 HSZ atom = DDEGetAtom(item);
2bda0e17 734
5bd3a2da
VZ
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;
2bda0e17
KB
747}
748
749// Calls that SERVER can make
50c549b9
VZ
750bool wxDDEConnection::DoAdvise(const wxString& item,
751 const void *data,
752 size_t size,
753 wxIPCFormat format)
2bda0e17 754{
5bd3a2da
VZ
755 HSZ item_atom = DDEGetAtom(item);
756 HSZ topic_atom = DDEGetAtom(m_topicName);
f010ad48 757 m_sendingData = data; // mrf: potential for scope problems here?
5bd3a2da 758 m_dataSize = size;
9d860992
JS
759 // wxIPC_PRIVATE does not succeed, so use text instead
760 m_dataType = format == wxIPC_PRIVATE ? wxIPC_TEXT : format;
2bda0e17 761
5bd3a2da
VZ
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;
2bda0e17
KB
769}
770
5bd3a2da
VZ
771// ----------------------------------------------------------------------------
772// _DDECallback
773// ----------------------------------------------------------------------------
2bda0e17
KB
774
775#define DDERETURN HDDEDATA
776
5bd3a2da
VZ
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)
2bda0e17 788 {
5bd3a2da
VZ
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;
cdd64cc0 805 return (DDERETURN)(DWORD)true;
5bd3a2da
VZ
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;
cdd64cc0 817 return (DDERETURN)(DWORD)true;
5bd3a2da
VZ
818 }
819 break;
820 }
821
822 case XTYP_DISCONNECT:
823 {
824 wxDDEConnection *connection = DDEFindConnection(hConv);
f010ad48 825 if (connection)
5bd3a2da 826 {
f010ad48
JS
827 connection->SetConnected( false );
828 if (connection->OnDisconnect())
829 {
830 DDEDeleteConnection(hConv); // Delete mapping: hConv => connection
cdd64cc0 831 return (DDERETURN)(DWORD)true;
f010ad48 832 }
5bd3a2da
VZ
833 }
834 break;
835 }
836
837 case XTYP_EXECUTE:
838 {
839 wxDDEConnection *connection = DDEFindConnection(hConv);
840
841 if (connection)
842 {
f010ad48
JS
843 DWORD len = DdeGetData(hData, NULL, 0, 0);
844
50c549b9 845 void *data = connection->GetBufferAtLeast(len);
f010ad48 846 wxASSERT_MSG(data != NULL,
9a83f860 847 wxT("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
f010ad48
JS
848
849 DdeGetData(hData, (LPBYTE)data, len, 0);
850
5bd3a2da 851 DdeFreeDataHandle(hData);
f010ad48 852
8b1350d4
VZ
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
5bd3a2da 862 if ( connection->OnExecute(connection->m_topicName,
f010ad48 863 data,
940ef730 864 (int)len,
8b1350d4 865 (wxIPCFormat)wFmt) )
5bd3a2da
VZ
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
50c549b9
VZ
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);
5bd3a2da
VZ
887 if (data)
888 {
50c549b9
VZ
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 }
5bd3a2da
VZ
902
903 HDDEDATA handle = DdeCreateDataHandle(DDEIdInst,
904 (LPBYTE)data,
9d860992 905 user_size,
5bd3a2da
VZ
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
f010ad48
JS
924 DWORD len = DdeGetData(hData, NULL, 0, 0);
925
50c549b9 926 void *data = connection->GetBufferAtLeast(len);
f010ad48 927 wxASSERT_MSG(data != NULL,
9a83f860 928 wxT("Buffer too small in _DDECallback (XTYP_POKE)") );
f010ad48
JS
929
930 DdeGetData(hData, (LPBYTE)data, len, 0);
931
5bd3a2da
VZ
932 DdeFreeDataHandle(hData);
933
934 connection->OnPoke(connection->m_topicName,
935 item_name,
f010ad48 936 data,
940ef730 937 (int)len,
5bd3a2da
VZ
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,
9d860992 988 connection->m_dataSize,
5bd3a2da
VZ
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
f010ad48
JS
1011 DWORD len = DdeGetData(hData, NULL, 0, 0);
1012
50c549b9 1013 void *data = connection->GetBufferAtLeast(len);
f010ad48 1014 wxASSERT_MSG(data != NULL,
9a83f860 1015 wxT("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
f010ad48
JS
1016
1017 DdeGetData(hData, (LPBYTE)data, len, 0);
1018
5bd3a2da
VZ
1019 DdeFreeDataHandle(hData);
1020 if ( connection->OnAdvise(connection->m_topicName,
1021 item_name,
f010ad48 1022 data,
940ef730 1023 (int)len,
5bd3a2da
VZ
1024 (wxIPCFormat) wFmt) )
1025 {
1026 return (DDERETURN)(DWORD)DDE_FACK;
1027 }
1028 }
1029
1030 return (DDERETURN)DDE_FNOTPROCESSED;
1031 }
2bda0e17
KB
1032 }
1033
5bd3a2da
VZ
1034 return (DDERETURN)0;
1035}
2bda0e17 1036
5bd3a2da
VZ
1037// ----------------------------------------------------------------------------
1038// DDE strings and atoms
1039// ----------------------------------------------------------------------------
2bda0e17 1040
5bd3a2da 1041// Atom table stuff
df5168c4 1042static HSZ DDEAddAtom(const wxString& str)
5bd3a2da 1043{
df5168c4
MB
1044 HSZ atom = DDEAtomFromString(str);
1045 wxAtomTable[str] = atom;
5bd3a2da
VZ
1046 return atom;
1047}
2bda0e17 1048
df5168c4 1049static HSZ DDEGetAtom(const wxString& str)
5bd3a2da 1050{
df5168c4
MB
1051 wxAtomMap::iterator it = wxAtomTable.find(str);
1052
1053 if (it != wxAtomTable.end())
1054 return it->second;
1055
1056 return DDEAddAtom(str);
5bd3a2da 1057}
2bda0e17 1058
0ebb0e5c
DS
1059/* atom <-> strings
1060The returned handle has to be freed by the caller (using
1061(static) DDEFreeString).
1062*/
5bd3a2da
VZ
1063static HSZ DDEAtomFromString(const wxString& s)
1064{
9a83f860 1065 wxASSERT_MSG( DDEIdInst, wxT("DDE not initialized") );
5bd3a2da 1066
c9f78968 1067 HSZ hsz = DdeCreateStringHandle(DDEIdInst, (wxChar*)s.wx_str(), DDE_CP);
5bd3a2da 1068 if ( !hsz )
2bda0e17 1069 {
5bd3a2da 1070 DDELogError(_("Failed to create DDE string"));
2bda0e17
KB
1071 }
1072
5bd3a2da
VZ
1073 return hsz;
1074}
2bda0e17 1075
5bd3a2da
VZ
1076static wxString DDEStringFromAtom(HSZ hsz)
1077{
1078 // all DDE strings are normally limited to 255 bytes
1079 static const size_t len = 256;
2bda0e17 1080
5bd3a2da 1081 wxString s;
de564874 1082 (void)DdeQueryString(DDEIdInst, hsz, wxStringBuffer(s, len), len, DDE_CP);
2bda0e17 1083
5bd3a2da
VZ
1084 return s;
1085}
2bda0e17 1086
0ebb0e5c
DS
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
5bd3a2da
VZ
1096// ----------------------------------------------------------------------------
1097// error handling
1098// ----------------------------------------------------------------------------
2bda0e17 1099
5bd3a2da
VZ
1100static void DDELogError(const wxString& s, UINT error)
1101{
1102 if ( !error )
2bda0e17 1103 {
5bd3a2da 1104 error = DdeGetLastError(DDEIdInst);
2bda0e17 1105 }
2bda0e17 1106
9a83f860 1107 wxLogError(s + wxT(": ") + DDEGetErrorMsg(error));
2bda0e17
KB
1108}
1109
5bd3a2da 1110static wxString DDEGetErrorMsg(UINT error)
2bda0e17 1111{
5bd3a2da
VZ
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:
f83e3685 1129 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
1130 break;
1131 case DMLERR_DLL_USAGE:
f83e3685 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.");
5bd3a2da
VZ
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:
f83e3685 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.");
5bd3a2da
VZ
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:
f83e3685 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.");
5bd3a2da
VZ
1172 break;
1173 default:
1174 err.Printf(_("Unknown DDE error %08x"), error);
1175 }
2bda0e17 1176
5bd3a2da 1177 return err;
2bda0e17
KB
1178}
1179
1180#endif
47d67540 1181 // wxUSE_IPC