]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/dde.cpp
no message
[wxWidgets.git] / src / msw / dde.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: dde.cpp
3// Purpose: DDE classes
4// Author: Julian Smart
5// Modified by:
6// Created: 01/02/97
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart and Markus Holzem
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12#ifdef __GNUG__
13#pragma implementation "dde.h"
14#endif
15
16// For compilers that support precompilation, includes "wx.h".
17#include "wx/wxprec.h"
18
19#ifdef __BORLANDC__
20#pragma hdrstop
21#endif
22
23#ifndef WX_PRECOMP
24#include "wx/defs.h"
25#endif
26
27#if wxUSE_IPC
28
29#ifndef WX_PRECOMP
30#include "wx/utils.h"
31#include "wx/app.h"
32#endif
33
34#include "wx/msw/private.h"
35#include "wx/dde.h"
36
37#ifdef __GNUWIN32__
38#include "wx/msw/gnuwin32/extra.h"
39#endif
40
41#include <windows.h>
42#include <ddeml.h>
43#include <string.h>
44
45#ifdef __WIN32__
46#define _EXPORT /**/
47#else
48#define _EXPORT _export
49#endif
50
51#if !USE_SHARED_LIBRARY
52IMPLEMENT_DYNAMIC_CLASS(wxDDEServer, wxServerBase)
53IMPLEMENT_DYNAMIC_CLASS(wxDDEClient, wxClientBase)
54IMPLEMENT_CLASS(wxDDEConnection, wxConnectionBase)
55#endif
56
57static wxDDEConnection *DDEFindConnection(HCONV hConv);
58static void DDEDeleteConnection(HCONV hConv);
59static wxDDEServer *DDEFindServer(const wxString& s);
60
61extern "C" HDDEDATA EXPENTRY _EXPORT _DDECallback(
62WORD wType,
63WORD wFmt,
64HCONV hConv,
65HSZ hsz1,
66HSZ hsz2,
67HDDEDATA hData,
68DWORD lData1,
69DWORD lData2);
70
71// Add topic name to atom table before using in conversations
72static HSZ DDEAddAtom(const wxString& string);
73static HSZ DDEGetAtom(const wxString& string);
74static void DDEPrintError(void);
75
76static DWORD DDEIdInst = 0L;
77static wxDDEConnection *DDECurrentlyConnecting = NULL;
78
79static wxList wxAtomTable(wxKEY_STRING);
80static wxList wxDDEClientObjects;
81static wxList wxDDEServerObjects;
82
83char *DDEDefaultIPCBuffer = NULL;
84int DDEDefaultIPCBufferSize = 0;
85
86/*
87 * Initialization
88 *
89 */
90
91static bool DDEInitialized = FALSE;
92
93void wxDDEInitialize()
94{
95 if (DDEInitialized)
96 return;
97 DDEInitialized = TRUE;
98
99 // Should insert filter flags
100 DdeInitialize(&DDEIdInst, (PFNCALLBACK)MakeProcInstance(
101 (FARPROC)_DDECallback, wxGetInstance()),
102 APPCLASS_STANDARD,
103 0L);
104}
105
106/*
107 * CleanUp
108 */
109
110void wxDDECleanUp()
111{
112 if (DDEIdInst != 0)
113 {
114 DdeUninitialize(DDEIdInst);
115 DDEIdInst = 0;
116 }
117 if (DDEDefaultIPCBuffer)
118 delete [] DDEDefaultIPCBuffer ;
119}
120
121// Global find connection
122static wxDDEConnection *DDEFindConnection(HCONV hConv)
123{
124 wxNode *node = wxDDEServerObjects.First();
125 wxDDEConnection *found = NULL;
126 while (node && !found)
127 {
128 wxDDEServer *object = (wxDDEServer *)node->Data();
129 found = object->FindConnection((WXHCONV) hConv);
130 node = node->Next();
131 }
132 if (found)
133 return found;
134
135 node = wxDDEClientObjects.First();
136 while (node && !found)
137 {
138 wxDDEClient *object = (wxDDEClient *)node->Data();
139 found = object->FindConnection((WXHCONV) hConv);
140 node = node->Next();
141 }
142 return found;
143}
144
145// Global delete connection
146static void DDEDeleteConnection(HCONV hConv)
147{
148 wxNode *node = wxDDEServerObjects.First();
149 bool found = FALSE;
150 while (node && !found)
151 {
152 wxDDEServer *object = (wxDDEServer *)node->Data();
153 found = object->DeleteConnection((WXHCONV) hConv);
154 node = node->Next();
155 }
156 if (found)
157 return;
158
159 node = wxDDEServerObjects.First();
160 while (node && !found)
161 {
162 wxDDEClient *object = (wxDDEClient *)node->Data();
163 found = object->DeleteConnection((WXHCONV) hConv);
164 node = node->Next();
165 }
166}
167
168// Find a server from a service name
169static wxDDEServer *DDEFindServer(const wxString& s)
170{
171 wxNode *node = wxDDEServerObjects.First();
172 wxDDEServer *found = NULL;
173 while (node && !found)
174 {
175 wxDDEServer *object = (wxDDEServer *)node->Data();
176
177 if (object->GetServiceName() == s)
178 found = object;
179 else node = node->Next();
180 }
181 return found;
182}
183
184/*
185 * Server
186 *
187 */
188
189wxDDEServer::wxDDEServer(void)
190{
191 m_serviceName = "";
192 wxDDEServerObjects.Append(this);
193}
194
195bool wxDDEServer::Create(const wxString& server_name)
196{
197 m_serviceName = server_name;
198 HSZ serviceName = DdeCreateStringHandle(DDEIdInst, (char*) (const char *)server_name, CP_WINANSI);
199
200 if (DdeNameService(DDEIdInst, serviceName, NULL, DNS_REGISTER) == 0)
201 {
202 DDEPrintError();
203 return FALSE;
204 }
205 return TRUE;
206}
207
208wxDDEServer::~wxDDEServer(void)
209{
210 if (m_serviceName != "")
211 {
212 HSZ serviceName = DdeCreateStringHandle(DDEIdInst, (char*) (const char *)m_serviceName, CP_WINANSI);
213 if (DdeNameService(DDEIdInst, serviceName, NULL, DNS_UNREGISTER) == 0)
214 {
215 DDEPrintError();
216 }
217 }
218 wxDDEServerObjects.DeleteObject(this);
219
220 wxNode *node = m_connections.First();
221 while (node)
222 {
223 wxDDEConnection *connection = (wxDDEConnection *)node->Data();
224 wxNode *next = node->Next();
225 connection->OnDisconnect(); // May delete the node implicitly
226 node = next;
227 }
228
229 // If any left after this, delete them
230 node = m_connections.First();
231 while (node)
232 {
233 wxDDEConnection *connection = (wxDDEConnection *)node->Data();
234 wxNode *next = node->Next();
235 delete connection;
236 node = next;
237 }
238}
239
240wxConnectionBase *wxDDEServer::OnAcceptConnection(const wxString& /* topic */)
241{
242 return new wxDDEConnection;
243}
244
245wxDDEConnection *wxDDEServer::FindConnection(WXHCONV conv)
246{
247 wxNode *node = m_connections.First();
248 wxDDEConnection *found = NULL;
249 while (node && !found)
250 {
251 wxDDEConnection *connection = (wxDDEConnection *)node->Data();
252 if (connection->m_hConv == conv)
253 found = connection;
254 else node = node->Next();
255 }
256 return found;
257}
258
259// Only delete the entry in the map, not the actual connection
260bool wxDDEServer::DeleteConnection(WXHCONV conv)
261{
262 wxNode *node = m_connections.First();
263 bool found = FALSE;
264 while (node && !found)
265 {
266 wxDDEConnection *connection = (wxDDEConnection *)node->Data();
267 if (connection->m_hConv == conv)
268 {
269 found = TRUE;
270 delete node;
271 }
272 else node = node->Next();
273 }
274 return found;
275}
276
277
278/*
279 * Client
280 *
281 */
282
283
284wxDDEClient::wxDDEClient(void)
285{
286 wxDDEClientObjects.Append(this);
287}
288
289wxDDEClient::~wxDDEClient(void)
290{
291 wxDDEClientObjects.DeleteObject(this);
292 wxNode *node = m_connections.First();
293 while (node)
294 {
295 wxDDEConnection *connection = (wxDDEConnection *)node->Data();
296 delete connection; // Deletes the node implicitly (see ~wxDDEConnection)
297 node = m_connections.First();
298 }
299}
300
301bool wxDDEClient::ValidHost(const wxString& /* host */)
302{
303 return TRUE;
304}
305
306wxConnectionBase *wxDDEClient::MakeConnection(const wxString& /* host */, const wxString& server_name, const wxString& topic)
307{
308 HSZ serviceName = DdeCreateStringHandle(DDEIdInst, (char*) (const char *)server_name, CP_WINANSI);
309 HSZ topic_atom = DdeCreateStringHandle(DDEIdInst, (char*) (const char *)topic, CP_WINANSI);
310
311 HCONV hConv = DdeConnect(DDEIdInst, serviceName, topic_atom, (PCONVCONTEXT)NULL);
312 if (hConv == NULL)
313 return NULL;
314 else
315 {
316 wxDDEConnection *connection = (wxDDEConnection*) OnMakeConnection();
317 if (connection)
318 {
319 connection->m_hConv = (WXHCONV) hConv;
320 connection->m_topicName = topic;
321 connection->m_client = this;
322 m_connections.Append(connection);
323 return connection;
324 }
325 else return NULL;
326 }
327}
328
329wxConnectionBase *wxDDEClient::OnMakeConnection(void)
330{
331 return new wxDDEConnection;
332}
333
334wxDDEConnection *wxDDEClient::FindConnection(WXHCONV conv)
335{
336 wxNode *node = m_connections.First();
337 wxDDEConnection *found = NULL;
338 while (node && !found)
339 {
340 wxDDEConnection *connection = (wxDDEConnection *)node->Data();
341 if (connection->m_hConv == conv)
342 found = connection;
343 else node = node->Next();
344 }
345 return found;
346}
347
348// Only delete the entry in the map, not the actual connection
349bool wxDDEClient::DeleteConnection(WXHCONV conv)
350{
351 wxNode *node = m_connections.First();
352 bool found = FALSE;
353 while (node && !found)
354 {
355 wxDDEConnection *connection = (wxDDEConnection *)node->Data();
356 if (connection->m_hConv == conv)
357 {
358 found = TRUE;
359 delete node;
360 }
361 else node = node->Next();
362 }
363 return found;
364}
365
366/*
367 * Connection
368 */
369
370wxDDEConnection::wxDDEConnection(char *buffer, int size)
371{
372 if (buffer == NULL)
373 {
374 if (DDEDefaultIPCBuffer == NULL)
375 DDEDefaultIPCBuffer = new char[DDEDefaultIPCBufferSize];
376 m_bufPtr = DDEDefaultIPCBuffer;
377 m_bufSize = DDEDefaultIPCBufferSize;
378 }
379 else
380 {
381 m_bufPtr = buffer;
382 m_bufSize = size;
383 }
384
385 m_topicName = "";
386
387 m_client = NULL;
388 m_server = NULL;
389
390 m_hConv = 0;
391 m_sendingData = NULL;
392}
393
394wxDDEConnection::wxDDEConnection(void)
395{
396 m_hConv = 0;
397 m_sendingData = NULL;
398 m_server = NULL;
399 m_client = NULL;
400 if (DDEDefaultIPCBuffer == NULL)
401 DDEDefaultIPCBuffer = new char[DDEDefaultIPCBufferSize];
402
403 m_bufPtr = DDEDefaultIPCBuffer;
404 m_bufSize = DDEDefaultIPCBufferSize;
405 m_topicName = "";
406}
407
408wxDDEConnection::~wxDDEConnection(void)
409{
410 if (m_server)
411 m_server->GetConnections().DeleteObject(this);
412 else
413 m_client->GetConnections().DeleteObject(this);
414}
415
416// Calls that CLIENT can make
417bool wxDDEConnection::Disconnect(void)
418{
419 DDEDeleteConnection((HCONV) m_hConv);
420 return (DdeDisconnect((HCONV) m_hConv) != 0);
421}
422
423bool wxDDEConnection::Execute(char *data, int size, wxDataFormat format)
424{
425 DWORD result;
426 if (size < 0)
427 size = strlen(data);
428
429 size ++;
430
431 return (DdeClientTransaction((LPBYTE)data, size, (HCONV) m_hConv,
432 NULL, format, XTYP_EXECUTE, 5000, &result) ? TRUE : FALSE);
433}
434
435char *wxDDEConnection::Request(const wxString& item, int *size, wxDataFormat format)
436{
437 DWORD result;
438 HSZ atom = DDEGetAtom(item);
439
440 HDDEDATA returned_data = DdeClientTransaction(NULL, 0, (HCONV) m_hConv,
441 atom, format, XTYP_REQUEST, 5000, &result);
442
443 DWORD len = DdeGetData(returned_data, (LPBYTE)(m_bufPtr), m_bufSize, 0);
444
445 DdeFreeDataHandle(returned_data);
446
447 if (size) *size = (int)len;
448 if (len > 0)
449 {
450 return m_bufPtr;
451 }
452 else return NULL;
453}
454
455bool wxDDEConnection::Poke(const wxString& item, char *data, int size, wxDataFormat format)
456{
457 DWORD result;
458 if (size < 0)
459 size = strlen(data);
460
461 size ++;
462
463 HSZ item_atom = DDEGetAtom(item);
464 return (DdeClientTransaction((LPBYTE)data, size, (HCONV) m_hConv,
465 item_atom, format, XTYP_POKE, 5000, &result) ? TRUE : FALSE);
466}
467
468bool wxDDEConnection::StartAdvise(const wxString& item)
469{
470 DWORD result;
471 HSZ atom = DDEGetAtom(item);
472
473 return (DdeClientTransaction(NULL, 0, (HCONV) m_hConv,
474 atom, CF_TEXT, XTYP_ADVSTART, 5000, &result) ? TRUE : FALSE);
475}
476
477bool wxDDEConnection::StopAdvise(const wxString& item)
478{
479 DWORD result;
480 HSZ atom = DDEGetAtom(item);
481
482 return (DdeClientTransaction(NULL, 0, (HCONV) m_hConv,
483 atom, CF_TEXT, XTYP_ADVSTOP, 5000, &result) ? TRUE : FALSE);
484}
485
486// Calls that SERVER can make
487bool wxDDEConnection::Advise(const wxString& item, char *data, int size, wxDataFormat format)
488{
489 if (size < 0)
490 size = strlen(data);
491
492 size ++;
493
494 HSZ item_atom = DDEGetAtom(item);
495 HSZ topic_atom = DDEGetAtom(m_topicName);
496 m_sendingData = data;
497 m_dataSize = size;
498 m_dataType = format;
499 return (DdePostAdvise(DDEIdInst, topic_atom, item_atom) != 0);
500}
501
502bool wxDDEConnection::OnDisconnect(void)
503{
504 delete this;
505 return TRUE;
506}
507
508
509#define DDERETURN HDDEDATA
510
511HDDEDATA EXPENTRY _EXPORT _DDECallback(
512WORD wType,
513WORD wFmt,
514HCONV hConv,
515HSZ hsz1,
516HSZ hsz2,
517HDDEDATA hData,
518DWORD /* lData1 */,
519DWORD /* lData2 */)
520{
521 switch (wType)
522 {
523 case XTYP_CONNECT:
524 {
525 char topic_buf[100];
526 char server_buf[100];
527 DdeQueryString(DDEIdInst, hsz1, (LPSTR)topic_buf, sizeof(topic_buf),
528 CP_WINANSI);
529 DdeQueryString(DDEIdInst, hsz2, (LPSTR)server_buf, sizeof(topic_buf),
530 CP_WINANSI);
531 wxDDEServer *server = DDEFindServer(server_buf);
532 if (server)
533 {
534 wxDDEConnection *connection =
535 (wxDDEConnection*) server->OnAcceptConnection(wxString(topic_buf));
536 if (connection)
537 {
538 connection->m_server = server;
539 server->GetConnections().Append(connection);
540 connection->m_hConv = 0;
541 connection->m_topicName = topic_buf;
542 DDECurrentlyConnecting = connection;
543 return (DDERETURN)TRUE;
544 }
545 }
546 else return (DDERETURN)0;
547 break;
548 }
549
550 case XTYP_CONNECT_CONFIRM:
551 {
552 if (DDECurrentlyConnecting)
553 {
554 DDECurrentlyConnecting->m_hConv = (WXHCONV) hConv;
555 DDECurrentlyConnecting = NULL;
556 return (DDERETURN)TRUE;
557 }
558 else return 0;
559 break;
560 }
561
562 case XTYP_DISCONNECT:
563 {
564 wxDDEConnection *connection = DDEFindConnection(hConv);
565 if (connection && connection->OnDisconnect())
566 {
567 DDEDeleteConnection(hConv); // Delete mapping: hConv => connection
568 return (DDERETURN)TRUE;
569 }
570 else return (DDERETURN)0;
571 break;
572 }
573
574 case XTYP_EXECUTE:
575 {
576 wxDDEConnection *connection = DDEFindConnection(hConv);
577
578 if (connection)
579 {
580 DWORD len = DdeGetData(hData, (LPBYTE)(connection->m_bufPtr), connection->m_bufSize, 0);
581 DdeFreeDataHandle(hData);
582 if (connection->OnExecute(connection->m_topicName, connection->m_bufPtr, (int)len, (wxDataFormat) wFmt))
583 return (DDERETURN)DDE_FACK;
584 else
585 return (DDERETURN)DDE_FNOTPROCESSED;
586 } else return (DDERETURN)DDE_FNOTPROCESSED;
587 break;
588 }
589
590 case XTYP_REQUEST:
591 {
592 wxDDEConnection *connection = DDEFindConnection(hConv);
593
594 if (connection)
595 {
596 char item_name[200];
597 DdeQueryString(DDEIdInst, hsz2, (LPSTR)item_name, sizeof(item_name),
598 CP_WINANSI);
599
600 int user_size = -1;
601 char *data = connection->OnRequest(connection->m_topicName, wxString(item_name), &user_size, (wxDataFormat) wFmt);
602 if (data)
603 {
604 if (user_size < 0) user_size = strlen(data);
605
606 HDDEDATA handle = DdeCreateDataHandle(DDEIdInst,
607 (LPBYTE)data, user_size + 1, 0, hsz2, wFmt, 0);
608 return (DDERETURN)handle;
609 } else return (DDERETURN)0;
610 } else return (DDERETURN)0;
611 break;
612 }
613
614 case XTYP_POKE:
615 {
616 wxDDEConnection *connection = DDEFindConnection(hConv);
617
618 if (connection)
619 {
620 char item_name[200];
621 DdeQueryString(DDEIdInst, hsz2, (LPSTR)item_name, sizeof(item_name),
622 CP_WINANSI);
623 DWORD len = DdeGetData(hData, (LPBYTE)(connection->m_bufPtr), connection->m_bufSize, 0);
624 DdeFreeDataHandle(hData);
625 connection->OnPoke(connection->m_topicName, wxString(item_name), connection->m_bufPtr, (int)len, (wxDataFormat) wFmt);
626 return (DDERETURN)DDE_FACK;
627 } else return (DDERETURN)DDE_FNOTPROCESSED;
628 break;
629 }
630
631 case XTYP_ADVSTART:
632 {
633 wxDDEConnection *connection = DDEFindConnection(hConv);
634
635 if (connection)
636 {
637 char item_name[200];
638 DdeQueryString(DDEIdInst, hsz2, (LPSTR)item_name, sizeof(item_name),
639 CP_WINANSI);
640
641 return (DDERETURN)connection->OnStartAdvise(connection->m_topicName, wxString(item_name));
642 } else return (DDERETURN)0;
643 break;
644 }
645
646 case XTYP_ADVSTOP:
647 {
648 wxDDEConnection *connection = DDEFindConnection(hConv);
649
650 if (connection)
651 {
652 char item_name[200];
653 DdeQueryString(DDEIdInst, hsz2, (LPSTR)item_name, sizeof(item_name),
654 CP_WINANSI);
655 return (DDERETURN)connection->OnStopAdvise(connection->m_topicName, wxString(item_name));
656 } else return (DDERETURN)0;
657 break;
658 }
659
660 case XTYP_ADVREQ:
661 {
662 wxDDEConnection *connection = DDEFindConnection(hConv);
663
664 if (connection && connection->m_sendingData)
665 {
666 HDDEDATA data = DdeCreateDataHandle(DDEIdInst,
667 (LPBYTE)connection->m_sendingData,
668 connection->m_dataSize, 0, hsz2, connection->m_dataType, 0);
669 connection->m_sendingData = NULL;
670 return (DDERETURN)data;
671 } else return (DDERETURN)NULL;
672 break;
673 }
674
675 case XTYP_ADVDATA:
676 {
677 wxDDEConnection *connection = DDEFindConnection(hConv);
678
679 if (connection)
680 {
681 char item_name[200];
682 DdeQueryString(DDEIdInst, hsz2, (LPSTR)item_name, sizeof(item_name),
683 CP_WINANSI);
684
685 DWORD len = DdeGetData(hData, (LPBYTE)(connection->m_bufPtr), connection->m_bufSize, 0);
686 DdeFreeDataHandle(hData);
687 if (connection->OnAdvise(connection->m_topicName, wxString(item_name), connection->m_bufPtr, (int)len, (wxDataFormat) wFmt))
688 return (DDERETURN)DDE_FACK;
689 else
690 return (DDERETURN)DDE_FNOTPROCESSED;
691 } else return (DDERETURN)DDE_FNOTPROCESSED;
692 break;
693 }
694 }
695 return 0;
696}
697
698// Atom table stuff
699static HSZ DDEAddAtom(const wxString& string)
700{
701 HSZ atom = DdeCreateStringHandle(DDEIdInst, (char*) (const char *)string, CP_WINANSI);
702 wxAtomTable.Append(string, (wxObject *)atom);
703 return atom;
704}
705
706static HSZ DDEGetAtom(const wxString& string)
707{
708 wxNode *node = wxAtomTable.Find(string);
709 if (node)
710 return (HSZ)node->Data();
711 else
712 {
713 DDEAddAtom(string);
714 return (HSZ)(wxAtomTable.Find(string)->Data());
715 }
716}
717
718void DDEPrintError(void)
719{
720 char *err = NULL;
721 switch (DdeGetLastError(DDEIdInst))
722 {
723 case DMLERR_ADVACKTIMEOUT:
724 err = "A request for a synchronous advise transaction has timed out.";
725 break;
726 case DMLERR_BUSY:
727 err = "The response to the transaction caused the DDE_FBUSY bit to be set.";
728 break;
729 case DMLERR_DATAACKTIMEOUT:
730 err = "A request for a synchronous data transaction has timed out.";
731 break;
732 case DMLERR_DLL_NOT_INITIALIZED:
733 err = "A DDEML function was called without first calling the DdeInitialize function,\n\ror an invalid instance identifier\n\rwas passed to a DDEML function.";
734 break;
735 case DMLERR_DLL_USAGE:
736 err = "An application initialized as APPCLASS_MONITOR has\n\rattempted to perform a DDE transaction,\n\ror an application initialized as APPCMD_CLIENTONLY has \n\rattempted to perform server transactions.";
737 break;
738 case DMLERR_EXECACKTIMEOUT:
739 err = "A request for a synchronous execute transaction has timed out.";
740 break;
741 case DMLERR_INVALIDPARAMETER:
742 err = "A parameter failed to be validated by the DDEML.";
743 break;
744 case DMLERR_LOW_MEMORY:
745 err = "A DDEML application has created a prolonged race condition.";
746 break;
747 case DMLERR_MEMORY_ERROR:
748 err = "A memory allocation failed.";
749 break;
750 case DMLERR_NO_CONV_ESTABLISHED:
751 err = "A client's attempt to establish a conversation has failed.";
752 break;
753 case DMLERR_NOTPROCESSED:
754 err = "A transaction failed.";
755 break;
756 case DMLERR_POKEACKTIMEOUT:
757 err = "A request for a synchronous poke transaction has timed out.";
758 break;
759 case DMLERR_POSTMSG_FAILED:
760 err = "An internal call to the PostMessage function has failed. ";
761 break;
762 case DMLERR_REENTRANCY:
763 err = "Reentrancy problem.";
764 break;
765 case DMLERR_SERVER_DIED:
766 err = "A server-side transaction was attempted on a conversation\n\rthat was terminated by the client, or the server\n\rterminated before completing a transaction.";
767 break;
768 case DMLERR_SYS_ERROR:
769 err = "An internal error has occurred in the DDEML.";
770 break;
771 case DMLERR_UNADVACKTIMEOUT:
772 err = "A request to end an advise transaction has timed out.";
773 break;
774 case DMLERR_UNFOUND_QUEUE_ID:
775 err = "An invalid transaction identifier was passed to a DDEML function.\n\rOnce the application has returned from an XTYP_XACT_COMPLETE callback,\n\rthe transaction identifier for that callback is no longer valid.";
776 break;
777 default:
778 err = "Unrecognised error type.";
779 break;
780 }
781 MessageBox(NULL, (LPCSTR)err, "DDE Error", MB_OK | MB_ICONINFORMATION);
782}
783
784#endif
785 // wxUSE_IPC