]> git.saurik.com Git - wxWidgets.git/blame - src/msw/dde.cpp
Don't use wxCHECK_W32API_VERSION() in #if when it can be undefined.
[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)
0a8d6945 142IMPLEMENT_DYNAMIC_CLASS(wxDDEConnection, wxConnectionBase)
5bd3a2da
VZ
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;
5e9d3bf7
VZ
596
597 // We need to pass the size of the buffer to DdeClientTransaction() and
598 // not the length of the string.
599 realSize *= sizeof(wchar_t);
8b1350d4
VZ
600 }
601#else // !wxUSE_UNICODE
602 if ( format == wxIPC_UNICODETEXT )
603 {
604 conv = &wxConvLibc;
605 }
606 else if ( format == wxIPC_UTF8TEXT )
607 {
608 // we could implement this in theory but it's not obvious how to pass
609 // the format information and, basically, why bother -- just use
610 // Unicode build
9a83f860 611 wxFAIL_MSG( wxT("UTF-8 text not supported in ANSI build") );
8b1350d4
VZ
612
613 return false;
614 }
615 else // don't convert wxIPC_TEXT
616 {
617 realData = (LPBYTE)data;
618 realSize = size;
619 }
620
621 if ( conv )
622 {
623 const wchar_t * const wtext = (const wchar_t *)data;
624 const size_t len = size/sizeof(wchar_t);
625
626 realSize = conv->FromWChar(NULL, 0, wtext, len);
627 if ( realSize == wxCONV_FAILED )
628 return false;
629
630 realData = (LPBYTE)buffer.GetWriteBuf(realSize*sizeof(char));
631 if ( !realData )
632 return false;
633
634 realSize = conv->FromWChar((char*)realData, realSize, wtext, len);
635 if ( realSize == wxCONV_FAILED )
636 return false;
637 }
638#endif // wxUSE_UNICODE/!wxUSE_UNICODE
639
640 DWORD result;
641 bool ok = DdeClientTransaction(realData,
5e9d3bf7 642 realSize,
b81885a6
VZ
643 GetHConv(),
644 NULL,
645 // MSDN: if the transaction specified by
646 // the wType parameter does not pass data
647 // or is XTYP_EXECUTE, wFmt should be zero.
648 0,
649 XTYP_EXECUTE,
650 DDE_TIMEOUT,
651 &result) != 0;
6ba63600 652
5bd3a2da
VZ
653 if ( !ok )
654 {
9a83f860 655 DDELogError(wxT("DDE execute request failed"));
5bd3a2da 656 }
2bda0e17 657
5bd3a2da 658 return ok;
2bda0e17
KB
659}
660
50c549b9 661const void *wxDDEConnection::Request(const wxString& item, size_t *size, wxIPCFormat format)
2bda0e17 662{
5bd3a2da 663 DWORD result;
f010ad48 664
5bd3a2da
VZ
665 HSZ atom = DDEGetAtom(item);
666
667 HDDEDATA returned_data = DdeClientTransaction(NULL, 0,
668 GetHConv(),
669 atom, format,
670 XTYP_REQUEST,
671 DDE_TIMEOUT,
672 &result);
673 if ( !returned_data )
674 {
9a83f860 675 DDELogError(wxT("DDE data request failed"));
5bd3a2da
VZ
676
677 return NULL;
678 }
2bda0e17 679
f010ad48
JS
680 DWORD len = DdeGetData(returned_data, NULL, 0, 0);
681
50c549b9 682 void *data = GetBufferAtLeast(len);
f010ad48 683 wxASSERT_MSG(data != NULL,
9a83f860 684 wxT("Buffer too small in wxDDEConnection::Request") );
0ebb0e5c 685 (void) DdeGetData(returned_data, (LPBYTE)data, len, 0);
2bda0e17 686
0ebb0e5c 687 (void) DdeFreeDataHandle(returned_data);
2bda0e17 688
5bd3a2da 689 if (size)
50c549b9 690 *size = (size_t)len;
2bda0e17 691
f010ad48 692 return data;
2bda0e17
KB
693}
694
50c549b9 695bool wxDDEConnection::DoPoke(const wxString& item, const void *data, size_t size, wxIPCFormat format)
2bda0e17 696{
5bd3a2da 697 DWORD result;
2bda0e17 698
5bd3a2da 699 HSZ item_atom = DDEGetAtom(item);
d71cc120 700 bool ok = DdeClientTransaction((LPBYTE)data,
9d860992 701 size,
5bd3a2da
VZ
702 GetHConv(),
703 item_atom, format,
704 XTYP_POKE,
705 DDE_TIMEOUT,
706 &result) != 0;
707 if ( !ok )
708 {
709 DDELogError(_("DDE poke request failed"));
710 }
2bda0e17 711
5bd3a2da 712 return ok;
2bda0e17
KB
713}
714
715bool wxDDEConnection::StartAdvise(const wxString& item)
716{
5bd3a2da
VZ
717 DWORD result;
718 HSZ atom = DDEGetAtom(item);
2bda0e17 719
5bd3a2da
VZ
720 bool ok = DdeClientTransaction(NULL, 0,
721 GetHConv(),
722 atom, CF_TEXT,
723 XTYP_ADVSTART,
724 DDE_TIMEOUT,
725 &result) != 0;
726 if ( !ok )
727 {
728 DDELogError(_("Failed to establish an advise loop with DDE server"));
729 }
730
731 return ok;
2bda0e17
KB
732}
733
734bool wxDDEConnection::StopAdvise(const wxString& item)
735{
5bd3a2da
VZ
736 DWORD result;
737 HSZ atom = DDEGetAtom(item);
2bda0e17 738
5bd3a2da
VZ
739 bool ok = DdeClientTransaction(NULL, 0,
740 GetHConv(),
741 atom, CF_TEXT,
742 XTYP_ADVSTOP,
743 DDE_TIMEOUT,
744 &result) != 0;
745 if ( !ok )
746 {
747 DDELogError(_("Failed to terminate the advise loop with DDE server"));
748 }
749
750 return ok;
2bda0e17
KB
751}
752
753// Calls that SERVER can make
50c549b9
VZ
754bool wxDDEConnection::DoAdvise(const wxString& item,
755 const void *data,
756 size_t size,
757 wxIPCFormat format)
2bda0e17 758{
5bd3a2da
VZ
759 HSZ item_atom = DDEGetAtom(item);
760 HSZ topic_atom = DDEGetAtom(m_topicName);
f010ad48 761 m_sendingData = data; // mrf: potential for scope problems here?
5bd3a2da 762 m_dataSize = size;
9d860992
JS
763 // wxIPC_PRIVATE does not succeed, so use text instead
764 m_dataType = format == wxIPC_PRIVATE ? wxIPC_TEXT : format;
2bda0e17 765
5bd3a2da
VZ
766 bool ok = DdePostAdvise(DDEIdInst, topic_atom, item_atom) != 0;
767 if ( !ok )
768 {
769 DDELogError(_("Failed to send DDE advise notification"));
770 }
771
772 return ok;
2bda0e17
KB
773}
774
5bd3a2da
VZ
775// ----------------------------------------------------------------------------
776// _DDECallback
777// ----------------------------------------------------------------------------
2bda0e17
KB
778
779#define DDERETURN HDDEDATA
780
5bd3a2da
VZ
781HDDEDATA EXPENTRY _EXPORT
782_DDECallback(WORD wType,
783 WORD wFmt,
784 HCONV hConv,
785 HSZ hsz1,
786 HSZ hsz2,
787 HDDEDATA hData,
788 DWORD WXUNUSED(lData1),
789 DWORD WXUNUSED(lData2))
790{
791 switch (wType)
2bda0e17 792 {
5bd3a2da
VZ
793 case XTYP_CONNECT:
794 {
795 wxString topic = DDEStringFromAtom(hsz1),
796 srv = DDEStringFromAtom(hsz2);
797 wxDDEServer *server = DDEFindServer(srv);
798 if (server)
799 {
800 wxDDEConnection *connection =
801 (wxDDEConnection*) server->OnAcceptConnection(topic);
802 if (connection)
803 {
804 connection->m_server = server;
805 server->GetConnections().Append(connection);
806 connection->m_hConv = 0;
807 connection->m_topicName = topic;
808 DDECurrentlyConnecting = connection;
cdd64cc0 809 return (DDERETURN)(DWORD)true;
5bd3a2da
VZ
810 }
811 }
812 break;
813 }
814
815 case XTYP_CONNECT_CONFIRM:
816 {
817 if (DDECurrentlyConnecting)
818 {
819 DDECurrentlyConnecting->m_hConv = (WXHCONV) hConv;
820 DDECurrentlyConnecting = NULL;
cdd64cc0 821 return (DDERETURN)(DWORD)true;
5bd3a2da
VZ
822 }
823 break;
824 }
825
826 case XTYP_DISCONNECT:
827 {
828 wxDDEConnection *connection = DDEFindConnection(hConv);
f010ad48 829 if (connection)
5bd3a2da 830 {
f010ad48
JS
831 connection->SetConnected( false );
832 if (connection->OnDisconnect())
833 {
834 DDEDeleteConnection(hConv); // Delete mapping: hConv => connection
cdd64cc0 835 return (DDERETURN)(DWORD)true;
f010ad48 836 }
5bd3a2da
VZ
837 }
838 break;
839 }
840
841 case XTYP_EXECUTE:
842 {
843 wxDDEConnection *connection = DDEFindConnection(hConv);
844
845 if (connection)
846 {
f010ad48
JS
847 DWORD len = DdeGetData(hData, NULL, 0, 0);
848
50c549b9 849 void *data = connection->GetBufferAtLeast(len);
f010ad48 850 wxASSERT_MSG(data != NULL,
9a83f860 851 wxT("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
f010ad48
JS
852
853 DdeGetData(hData, (LPBYTE)data, len, 0);
854
5bd3a2da 855 DdeFreeDataHandle(hData);
f010ad48 856
8b1350d4
VZ
857 // XTYP_EXECUTE can be used for text only and the text is
858 // always in ANSI format for ANSI build and Unicode format
859 // in Unicode build
860 #if wxUSE_UNICODE
861 wFmt = wxIPC_UNICODETEXT;
862 #else
863 wFmt = wxIPC_TEXT;
864 #endif
865
5bd3a2da 866 if ( connection->OnExecute(connection->m_topicName,
f010ad48 867 data,
940ef730 868 (int)len,
8b1350d4 869 (wxIPCFormat)wFmt) )
5bd3a2da
VZ
870 {
871 return (DDERETURN)(DWORD)DDE_FACK;
872 }
873 }
874
875 return (DDERETURN)DDE_FNOTPROCESSED;
876 }
877
878 case XTYP_REQUEST:
879 {
880 wxDDEConnection *connection = DDEFindConnection(hConv);
881
882 if (connection)
883 {
884 wxString item_name = DDEStringFromAtom(hsz2);
885
50c549b9
VZ
886 size_t user_size = wxNO_LEN;
887 const void *data = connection->OnRequest(connection->m_topicName,
888 item_name,
889 &user_size,
890 (wxIPCFormat)wFmt);
5bd3a2da
VZ
891 if (data)
892 {
50c549b9
VZ
893 if (user_size == wxNO_LEN)
894 switch (wFmt)
895 {
896 case wxIPC_TEXT:
897 case wxIPC_UTF8TEXT:
898 user_size = strlen((const char*)data) + 1; // includes final NUL
899 break;
900 case wxIPC_UNICODETEXT:
901 user_size = (wcslen((const wchar_t*)data) + 1) * sizeof(wchar_t); // includes final NUL
902 break;
903 default:
904 user_size = 0;
905 }
5bd3a2da
VZ
906
907 HDDEDATA handle = DdeCreateDataHandle(DDEIdInst,
908 (LPBYTE)data,
9d860992 909 user_size,
5bd3a2da
VZ
910 0,
911 hsz2,
912 wFmt,
913 0);
914 return (DDERETURN)handle;
915 }
916 }
917 break;
918 }
919
920 case XTYP_POKE:
921 {
922 wxDDEConnection *connection = DDEFindConnection(hConv);
923
924 if (connection)
925 {
926 wxString item_name = DDEStringFromAtom(hsz2);
927
f010ad48
JS
928 DWORD len = DdeGetData(hData, NULL, 0, 0);
929
50c549b9 930 void *data = connection->GetBufferAtLeast(len);
f010ad48 931 wxASSERT_MSG(data != NULL,
9a83f860 932 wxT("Buffer too small in _DDECallback (XTYP_POKE)") );
f010ad48
JS
933
934 DdeGetData(hData, (LPBYTE)data, len, 0);
935
5bd3a2da
VZ
936 DdeFreeDataHandle(hData);
937
938 connection->OnPoke(connection->m_topicName,
939 item_name,
f010ad48 940 data,
940ef730 941 (int)len,
5bd3a2da
VZ
942 (wxIPCFormat) wFmt);
943
944 return (DDERETURN)DDE_FACK;
945 }
946 else
947 {
948 return (DDERETURN)DDE_FNOTPROCESSED;
949 }
950 }
951
952 case XTYP_ADVSTART:
953 {
954 wxDDEConnection *connection = DDEFindConnection(hConv);
955
956 if (connection)
957 {
958 wxString item_name = DDEStringFromAtom(hsz2);
959
960 return (DDERETURN)connection->
961 OnStartAdvise(connection->m_topicName, item_name);
962 }
963
964 break;
965 }
966
967 case XTYP_ADVSTOP:
968 {
969 wxDDEConnection *connection = DDEFindConnection(hConv);
970
971 if (connection)
972 {
973 wxString item_name = DDEStringFromAtom(hsz2);
974
975 return (DDERETURN)connection->
976 OnStopAdvise(connection->m_topicName, item_name);
977 }
978
979 break;
980 }
981
982 case XTYP_ADVREQ:
983 {
984 wxDDEConnection *connection = DDEFindConnection(hConv);
985
986 if (connection && connection->m_sendingData)
987 {
988 HDDEDATA data = DdeCreateDataHandle
989 (
990 DDEIdInst,
991 (LPBYTE)connection->m_sendingData,
9d860992 992 connection->m_dataSize,
5bd3a2da
VZ
993 0,
994 hsz2,
995 connection->m_dataType,
996 0
997 );
998
999 connection->m_sendingData = NULL;
1000
1001 return (DDERETURN)data;
1002 }
1003
1004 break;
1005 }
1006
1007 case XTYP_ADVDATA:
1008 {
1009 wxDDEConnection *connection = DDEFindConnection(hConv);
1010
1011 if (connection)
1012 {
1013 wxString item_name = DDEStringFromAtom(hsz2);
1014
f010ad48
JS
1015 DWORD len = DdeGetData(hData, NULL, 0, 0);
1016
50c549b9 1017 void *data = connection->GetBufferAtLeast(len);
f010ad48 1018 wxASSERT_MSG(data != NULL,
9a83f860 1019 wxT("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
f010ad48
JS
1020
1021 DdeGetData(hData, (LPBYTE)data, len, 0);
1022
5bd3a2da
VZ
1023 DdeFreeDataHandle(hData);
1024 if ( connection->OnAdvise(connection->m_topicName,
1025 item_name,
f010ad48 1026 data,
940ef730 1027 (int)len,
5bd3a2da
VZ
1028 (wxIPCFormat) wFmt) )
1029 {
1030 return (DDERETURN)(DWORD)DDE_FACK;
1031 }
1032 }
1033
1034 return (DDERETURN)DDE_FNOTPROCESSED;
1035 }
2bda0e17
KB
1036 }
1037
5bd3a2da
VZ
1038 return (DDERETURN)0;
1039}
2bda0e17 1040
5bd3a2da
VZ
1041// ----------------------------------------------------------------------------
1042// DDE strings and atoms
1043// ----------------------------------------------------------------------------
2bda0e17 1044
5bd3a2da 1045// Atom table stuff
df5168c4 1046static HSZ DDEAddAtom(const wxString& str)
5bd3a2da 1047{
df5168c4
MB
1048 HSZ atom = DDEAtomFromString(str);
1049 wxAtomTable[str] = atom;
5bd3a2da
VZ
1050 return atom;
1051}
2bda0e17 1052
df5168c4 1053static HSZ DDEGetAtom(const wxString& str)
5bd3a2da 1054{
df5168c4
MB
1055 wxAtomMap::iterator it = wxAtomTable.find(str);
1056
1057 if (it != wxAtomTable.end())
1058 return it->second;
1059
1060 return DDEAddAtom(str);
5bd3a2da 1061}
2bda0e17 1062
0ebb0e5c
DS
1063/* atom <-> strings
1064The returned handle has to be freed by the caller (using
1065(static) DDEFreeString).
1066*/
5bd3a2da
VZ
1067static HSZ DDEAtomFromString(const wxString& s)
1068{
9a83f860 1069 wxASSERT_MSG( DDEIdInst, wxT("DDE not initialized") );
5bd3a2da 1070
c9f78968 1071 HSZ hsz = DdeCreateStringHandle(DDEIdInst, (wxChar*)s.wx_str(), DDE_CP);
5bd3a2da 1072 if ( !hsz )
2bda0e17 1073 {
5bd3a2da 1074 DDELogError(_("Failed to create DDE string"));
2bda0e17
KB
1075 }
1076
5bd3a2da
VZ
1077 return hsz;
1078}
2bda0e17 1079
5bd3a2da
VZ
1080static wxString DDEStringFromAtom(HSZ hsz)
1081{
1082 // all DDE strings are normally limited to 255 bytes
1083 static const size_t len = 256;
2bda0e17 1084
5bd3a2da 1085 wxString s;
de564874 1086 (void)DdeQueryString(DDEIdInst, hsz, wxStringBuffer(s, len), len, DDE_CP);
2bda0e17 1087
5bd3a2da
VZ
1088 return s;
1089}
2bda0e17 1090
0ebb0e5c
DS
1091static void DDEFreeString(HSZ hsz)
1092{
1093 // DS: Failure to free a string handle might indicate there's
1094 // some other severe error.
1095 bool ok = (::DdeFreeStringHandle(DDEIdInst, hsz) != 0);
1096 wxASSERT_MSG( ok, wxT("Failed to free DDE string handle") );
1097 wxUnusedVar(ok);
1098}
1099
5bd3a2da
VZ
1100// ----------------------------------------------------------------------------
1101// error handling
1102// ----------------------------------------------------------------------------
2bda0e17 1103
5bd3a2da
VZ
1104static void DDELogError(const wxString& s, UINT error)
1105{
1106 if ( !error )
2bda0e17 1107 {
5bd3a2da 1108 error = DdeGetLastError(DDEIdInst);
2bda0e17 1109 }
2bda0e17 1110
9a83f860 1111 wxLogError(s + wxT(": ") + DDEGetErrorMsg(error));
2bda0e17
KB
1112}
1113
5bd3a2da 1114static wxString DDEGetErrorMsg(UINT error)
2bda0e17 1115{
5bd3a2da
VZ
1116 wxString err;
1117 switch ( error )
1118 {
1119 case DMLERR_NO_ERROR:
1120 err = _("no DDE error.");
1121 break;
1122
1123 case DMLERR_ADVACKTIMEOUT:
1124 err = _("a request for a synchronous advise transaction has timed out.");
1125 break;
1126 case DMLERR_BUSY:
1127 err = _("the response to the transaction caused the DDE_FBUSY bit to be set.");
1128 break;
1129 case DMLERR_DATAACKTIMEOUT:
1130 err = _("a request for a synchronous data transaction has timed out.");
1131 break;
1132 case DMLERR_DLL_NOT_INITIALIZED:
f83e3685 1133 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
1134 break;
1135 case DMLERR_DLL_USAGE:
f83e3685 1136 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
1137 break;
1138 case DMLERR_EXECACKTIMEOUT:
1139 err = _("a request for a synchronous execute transaction has timed out.");
1140 break;
1141 case DMLERR_INVALIDPARAMETER:
1142 err = _("a parameter failed to be validated by the DDEML.");
1143 break;
1144 case DMLERR_LOW_MEMORY:
1145 err = _("a DDEML application has created a prolonged race condition.");
1146 break;
1147 case DMLERR_MEMORY_ERROR:
1148 err = _("a memory allocation failed.");
1149 break;
1150 case DMLERR_NO_CONV_ESTABLISHED:
1151 err = _("a client's attempt to establish a conversation has failed.");
1152 break;
1153 case DMLERR_NOTPROCESSED:
1154 err = _("a transaction failed.");
1155 break;
1156 case DMLERR_POKEACKTIMEOUT:
1157 err = _("a request for a synchronous poke transaction has timed out.");
1158 break;
1159 case DMLERR_POSTMSG_FAILED:
1160 err = _("an internal call to the PostMessage function has failed. ");
1161 break;
1162 case DMLERR_REENTRANCY:
1163 err = _("reentrancy problem.");
1164 break;
1165 case DMLERR_SERVER_DIED:
f83e3685 1166 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
1167 break;
1168 case DMLERR_SYS_ERROR:
1169 err = _("an internal error has occurred in the DDEML.");
1170 break;
1171 case DMLERR_UNADVACKTIMEOUT:
1172 err = _("a request to end an advise transaction has timed out.");
1173 break;
1174 case DMLERR_UNFOUND_QUEUE_ID:
f83e3685 1175 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
1176 break;
1177 default:
1178 err.Printf(_("Unknown DDE error %08x"), error);
1179 }
2bda0e17 1180
5bd3a2da 1181 return err;
2bda0e17
KB
1182}
1183
1184#endif
47d67540 1185 // wxUSE_IPC