]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dde.cpp
Cured DC/GDI object leak; listbox window proc restored from debugging
[wxWidgets.git] / src / msw / dde.cpp
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 USE_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
52 IMPLEMENT_DYNAMIC_CLASS(wxDDEServer, wxServerBase)
53 IMPLEMENT_DYNAMIC_CLASS(wxDDEClient, wxClientBase)
54 IMPLEMENT_CLASS(wxDDEConnection, wxConnectionBase)
55 #endif
56
57 static wxDDEConnection *DDEFindConnection(HCONV hConv);
58 static void DDEDeleteConnection(HCONV hConv);
59 static wxDDEServer *DDEFindServer(const wxString& s);
60
61 extern "C" HDDEDATA EXPENTRY _EXPORT _DDECallback(
62 WORD wType,
63 WORD wFmt,
64 HCONV hConv,
65 HSZ hsz1,
66 HSZ hsz2,
67 HDDEDATA hData,
68 DWORD lData1,
69 DWORD lData2);
70
71 // Add topic name to atom table before using in conversations
72 static HSZ DDEAddAtom(const wxString& string);
73 static HSZ DDEGetAtom(const wxString& string);
74 static void DDEPrintError(void);
75
76 static DWORD DDEIdInst = 0L;
77 static wxDDEConnection *DDECurrentlyConnecting = NULL;
78
79 static wxList wxAtomTable(wxKEY_STRING);
80 static wxList wxDDEClientObjects;
81 static wxList wxDDEServerObjects;
82
83 char *DDEDefaultIPCBuffer = NULL;
84 int DDEDefaultIPCBufferSize = 0;
85
86 /*
87 * Initialization
88 *
89 */
90
91 static bool DDEInitialized = FALSE;
92
93 void 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
110 void 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
122 static 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
146 static 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
169 static 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
189 wxDDEServer::wxDDEServer(void)
190 {
191 service_name = "";
192 wxDDEServerObjects.Append(this);
193 }
194
195 bool wxDDEServer::Create(const wxString& server_name)
196 {
197 service_name = 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
208 wxDDEServer::~wxDDEServer(void)
209 {
210 if (service_name != "")
211 {
212 HSZ serviceName = DdeCreateStringHandle(DDEIdInst, (char*) (const char *)service_name, CP_WINANSI);
213 if (DdeNameService(DDEIdInst, serviceName, NULL, DNS_UNREGISTER) == 0)
214 {
215 DDEPrintError();
216 }
217 }
218 wxDDEServerObjects.DeleteObject(this);
219
220 wxNode *node = 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 = 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
240 wxConnectionBase *wxDDEServer::OnAcceptConnection(const wxString& /* topic */)
241 {
242 return new wxDDEConnection;
243 }
244
245 wxDDEConnection *wxDDEServer::FindConnection(WXHCONV conv)
246 {
247 wxNode *node = connections.First();
248 wxDDEConnection *found = NULL;
249 while (node && !found)
250 {
251 wxDDEConnection *connection = (wxDDEConnection *)node->Data();
252 if (connection->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
260 bool wxDDEServer::DeleteConnection(WXHCONV conv)
261 {
262 wxNode *node = connections.First();
263 bool found = FALSE;
264 while (node && !found)
265 {
266 wxDDEConnection *connection = (wxDDEConnection *)node->Data();
267 if (connection->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
284 wxDDEClient::wxDDEClient(void)
285 {
286 wxDDEClientObjects.Append(this);
287 }
288
289 wxDDEClient::~wxDDEClient(void)
290 {
291 wxDDEClientObjects.DeleteObject(this);
292 wxNode *node = connections.First();
293 while (node)
294 {
295 wxDDEConnection *connection = (wxDDEConnection *)node->Data();
296 delete connection; // Deletes the node implicitly (see ~wxDDEConnection)
297 node = connections.First();
298 }
299 }
300
301 bool wxDDEClient::ValidHost(const wxString& /* host */)
302 {
303 return TRUE;
304 }
305
306 wxConnectionBase *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->hConv = (WXHCONV) hConv;
320 connection->topic_name = topic;
321 connection->client = this;
322 connections.Append(connection);
323 return connection;
324 }
325 else return NULL;
326 }
327 }
328
329 wxConnectionBase *wxDDEClient::OnMakeConnection(void)
330 {
331 return new wxDDEConnection;
332 }
333
334 wxDDEConnection *wxDDEClient::FindConnection(WXHCONV conv)
335 {
336 wxNode *node = connections.First();
337 wxDDEConnection *found = NULL;
338 while (node && !found)
339 {
340 wxDDEConnection *connection = (wxDDEConnection *)node->Data();
341 if (connection->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
349 bool wxDDEClient::DeleteConnection(WXHCONV conv)
350 {
351 wxNode *node = connections.First();
352 bool found = FALSE;
353 while (node && !found)
354 {
355 wxDDEConnection *connection = (wxDDEConnection *)node->Data();
356 if (connection->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
370 wxDDEConnection::wxDDEConnection(char *buffer, int size)
371 {
372 if (buffer == NULL)
373 {
374 if (DDEDefaultIPCBuffer == NULL)
375 DDEDefaultIPCBuffer = new char[DDEDefaultIPCBufferSize];
376 buf_ptr = DDEDefaultIPCBuffer;
377 buf_size = DDEDefaultIPCBufferSize;
378 }
379 else
380 {
381 buf_ptr = buffer;
382 buf_size = size;
383 }
384
385 topic_name = "";
386
387 client = NULL;
388 server = NULL;
389
390 hConv = 0;
391 sending_data = NULL;
392 }
393
394 wxDDEConnection::wxDDEConnection(void)
395 {
396 hConv = 0;
397 sending_data = NULL;
398 server = NULL;
399 client = NULL;
400 if (DDEDefaultIPCBuffer == NULL)
401 DDEDefaultIPCBuffer = new char[DDEDefaultIPCBufferSize];
402
403 buf_ptr = DDEDefaultIPCBuffer;
404 buf_size = DDEDefaultIPCBufferSize;
405 topic_name = "";
406 }
407
408 wxDDEConnection::~wxDDEConnection(void)
409 {
410 if (server)
411 server->GetConnections().DeleteObject(this);
412 else
413 client->GetConnections().DeleteObject(this);
414 }
415
416 // Calls that CLIENT can make
417 bool wxDDEConnection::Disconnect(void)
418 {
419 DDEDeleteConnection((HCONV) hConv);
420 return (DdeDisconnect((HCONV) hConv) != 0);
421 }
422
423 bool wxDDEConnection::Execute(char *data, int size, int format)
424 {
425 DWORD result;
426 if (size < 0)
427 size = strlen(data);
428
429 size ++;
430
431 return (DdeClientTransaction((LPBYTE)data, size, (HCONV) hConv,
432 NULL, format, XTYP_EXECUTE, 5000, &result) ? TRUE : FALSE);
433 }
434
435 char *wxDDEConnection::Request(const wxString& item, int *size, int format)
436 {
437 DWORD result;
438 HSZ atom = DDEGetAtom(item);
439
440 HDDEDATA returned_data = DdeClientTransaction(NULL, 0, (HCONV) hConv,
441 atom, format, XTYP_REQUEST, 5000, &result);
442
443 DWORD len = DdeGetData(returned_data, (LPBYTE)(buf_ptr), buf_size, 0);
444
445 DdeFreeDataHandle(returned_data);
446
447 if (size) *size = (int)len;
448 if (len > 0)
449 {
450 return buf_ptr;
451 }
452 else return NULL;
453 }
454
455 bool wxDDEConnection::Poke(const wxString& item, char *data, int size, int 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) hConv,
465 item_atom, format, XTYP_POKE, 5000, &result) ? TRUE : FALSE);
466 }
467
468 bool wxDDEConnection::StartAdvise(const wxString& item)
469 {
470 DWORD result;
471 HSZ atom = DDEGetAtom(item);
472
473 return (DdeClientTransaction(NULL, 0, (HCONV) hConv,
474 atom, CF_TEXT, XTYP_ADVSTART, 5000, &result) ? TRUE : FALSE);
475 }
476
477 bool wxDDEConnection::StopAdvise(const wxString& item)
478 {
479 DWORD result;
480 HSZ atom = DDEGetAtom(item);
481
482 return (DdeClientTransaction(NULL, 0, (HCONV) hConv,
483 atom, CF_TEXT, XTYP_ADVSTOP, 5000, &result) ? TRUE : FALSE);
484 }
485
486 // Calls that SERVER can make
487 bool wxDDEConnection::Advise(const wxString& item, char *data, int size, int 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(topic_name);
496 sending_data = data;
497 data_size = size;
498 data_type = format;
499 return (DdePostAdvise(DDEIdInst, topic_atom, item_atom) != 0);
500 }
501
502 bool wxDDEConnection::OnDisconnect(void)
503 {
504 delete this;
505 return TRUE;
506 }
507
508
509 #define DDERETURN HDDEDATA
510
511 HDDEDATA EXPENTRY _EXPORT _DDECallback(
512 WORD wType,
513 WORD wFmt,
514 HCONV hConv,
515 HSZ hsz1,
516 HSZ hsz2,
517 HDDEDATA hData,
518 DWORD /* lData1 */,
519 DWORD /* 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->server = server;
539 server->GetConnections().Append(connection);
540 connection->hConv = 0;
541 connection->topic_name = 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->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->buf_ptr), connection->buf_size, 0);
581 DdeFreeDataHandle(hData);
582 if (connection->OnExecute(connection->topic_name, connection->buf_ptr, (int)len, 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->topic_name, wxString(item_name), &user_size, 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->buf_ptr), connection->buf_size, 0);
624 DdeFreeDataHandle(hData);
625 connection->OnPoke(connection->topic_name, wxString(item_name), connection->buf_ptr, (int)len, 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->topic_name, 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->topic_name, 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->sending_data)
665 {
666 HDDEDATA data = DdeCreateDataHandle(DDEIdInst,
667 (LPBYTE)connection->sending_data,
668 connection->data_size, 0, hsz2, connection->data_type, 0);
669 connection->sending_data = 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->buf_ptr), connection->buf_size, 0);
686 DdeFreeDataHandle(hData);
687 if (connection->OnAdvise(connection->topic_name, wxString(item_name), connection->buf_ptr, (int)len, 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
699 static 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
706 static 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
718 void 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 // USE_IPC