]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/dde.cpp
Fix another off-by-1 bug in wxMBConv::ToWChar().
[wxWidgets.git] / src / msw / dde.cpp
index 8ee90711604289f4bee751057018a444f4a9af37..330ffa077d0f28cc912b2e7cdae838f25da3bd62 100644 (file)
 /////////////////////////////////////////////////////////////////////////////
-// Name:        dde.cpp
+// Name:        src/msw/dde.cpp
 // Purpose:     DDE classes
 // Author:      Julian Smart
 // Modified by:
 // Created:     01/02/97
 // RCS-ID:      $Id$
-// Copyright:   (c) Julian Smart and Markus Holzem
-// Licence:    wxWindows licence
+// Copyright:   (c) Julian Smart
+// Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
-#ifdef __GNUG__
-#pragma implementation "dde.h"
-#endif
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
 
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
 #ifdef __BORLANDC__
-#pragma hdrstop
-#endif
-
-#ifndef WX_PRECOMP
-#include "wx/defs.h"
+    #pragma hdrstop
 #endif
 
 #if wxUSE_IPC
 
 #ifndef WX_PRECOMP
-#include "wx/utils.h"
-#include "wx/app.h"
+    #include "wx/utils.h"
+    #include "wx/app.h"
+    #include "wx/hashmap.h"
+    #include "wx/module.h"
 #endif
 
-#include "wx/msw/private.h"
 #include "wx/dde.h"
+#include "wx/intl.h"
+#include "wx/buffer.h"
+#include "wx/strconv.h"
 
-#ifndef __TWIN32__
-#ifdef __GNUWIN32__
-#include "wx/msw/gnuwin32/extra.h"
-#endif
-#endif
+#include "wx/msw/private.h"
 
-#include <windows.h>
-#include <ddeml.h>
 #include <string.h>
+#include <ddeml.h>
+
+// ----------------------------------------------------------------------------
+// macros and constants
+// ----------------------------------------------------------------------------
 
 #ifdef __WIN32__
-#define _EXPORT /**/
+    #define _EXPORT
 #else
-#define _EXPORT _export
+    #define _EXPORT _export
 #endif
 
-#if !USE_SHARED_LIBRARY
-IMPLEMENT_DYNAMIC_CLASS(wxDDEServer, wxServerBase)
-IMPLEMENT_DYNAMIC_CLASS(wxDDEClient, wxClientBase)
-IMPLEMENT_CLASS(wxDDEConnection, wxConnectionBase)
+#if wxUSE_UNICODE
+    #define DDE_CP      CP_WINUNICODE
+#else
+    #define DDE_CP      CP_WINANSI
 #endif
 
+#define GetHConv()       ((HCONV)m_hConv)
+
+// default timeout for DDE operations (5sec)
+#define DDE_TIMEOUT     5000
+
+// ----------------------------------------------------------------------------
+// private functions
+// ----------------------------------------------------------------------------
+
 static wxDDEConnection *DDEFindConnection(HCONV hConv);
 static void DDEDeleteConnection(HCONV hConv);
 static wxDDEServer *DDEFindServer(const wxString& s);
 
-extern "C" HDDEDATA EXPENTRY _EXPORT _DDECallback(
-WORD wType,
-WORD wFmt,
-HCONV hConv,
-HSZ hsz1,
-HSZ hsz2,
-HDDEDATA hData,
-DWORD lData1,
-DWORD lData2);
+extern "C" HDDEDATA EXPENTRY _EXPORT _DDECallback(WORD wType,
+                                                  WORD wFmt,
+                                                  HCONV hConv,
+                                                  HSZ hsz1,
+                                                  HSZ hsz2,
+                                                  HDDEDATA hData,
+                                                  DWORD lData1,
+                                                  DWORD lData2);
 
 // Add topic name to atom table before using in conversations
 static HSZ DDEAddAtom(const wxString& string);
 static HSZ DDEGetAtom(const wxString& string);
-static void DDEPrintError(void);
+
+// string handles
+static HSZ DDEAtomFromString(const wxString& s);
+static wxString DDEStringFromAtom(HSZ hsz);
+static void DDEFreeString(HSZ hsz);
+
+// error handling
+static wxString DDEGetErrorMsg(UINT error);
+static void DDELogError(const wxString& s, UINT error = DMLERR_NO_ERROR);
+
+// ----------------------------------------------------------------------------
+// global variables
+// ----------------------------------------------------------------------------
+
+WX_DECLARE_STRING_HASH_MAP( HSZ, wxAtomMap );
 
 static DWORD DDEIdInst = 0L;
 static wxDDEConnection *DDECurrentlyConnecting = NULL;
+static wxAtomMap wxAtomTable;
+
+#include "wx/listimpl.cpp"
+
+WX_DEFINE_LIST(wxDDEClientList)
+WX_DEFINE_LIST(wxDDEServerList)
+WX_DEFINE_LIST(wxDDEConnectionList)
 
-static wxList wxAtomTable(wxKEY_STRING);
-static wxList wxDDEClientObjects;
-static wxList wxDDEServerObjects;
+static wxDDEClientList wxDDEClientObjects;
+static wxDDEServerList wxDDEServerObjects;
 
-char *DDEDefaultIPCBuffer = NULL;
-int DDEDefaultIPCBufferSize = 0;
+static bool DDEInitialized = false;
 
-/*
- * Initialization
- *
- */
+// ----------------------------------------------------------------------------
+// private classes
+// ----------------------------------------------------------------------------
 
-static bool DDEInitialized = FALSE;
+// A module to allow DDE cleanup without calling these functions
+// from app.cpp or from the user's application.
 
-void wxDDEInitialize()
+class wxDDEModule : public wxModule
 {
-  if (DDEInitialized)
-    return;
-  DDEInitialized = TRUE;
+public:
+    wxDDEModule() {}
+    bool OnInit() { return true; }
+    void OnExit() { wxDDECleanUp(); }
 
-  // Should insert filter flags
-  DdeInitialize(&DDEIdInst, (PFNCALLBACK)MakeProcInstance(
-               (FARPROC)_DDECallback, wxGetInstance()),
-               APPCLASS_STANDARD,
-               0L);
-}
+private:
+    DECLARE_DYNAMIC_CLASS(wxDDEModule)
+};
 
-/*
- * CleanUp
- */
+// ----------------------------------------------------------------------------
+// wxWin macros
+// ----------------------------------------------------------------------------
+
+IMPLEMENT_DYNAMIC_CLASS(wxDDEServer, wxServerBase)
+IMPLEMENT_DYNAMIC_CLASS(wxDDEClient, wxClientBase)
+IMPLEMENT_CLASS(wxDDEConnection, wxConnectionBase)
+IMPLEMENT_DYNAMIC_CLASS(wxDDEModule, wxModule)
+
+// ============================================================================
+// implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// initialization and cleanup
+// ----------------------------------------------------------------------------
+
+extern void wxDDEInitialize()
+{
+    if ( !DDEInitialized )
+    {
+        // Should insert filter flags
+        PFNCALLBACK callback = (PFNCALLBACK)
+            MakeProcInstance((FARPROC)_DDECallback, wxGetInstance());
+        UINT rc = DdeInitialize(&DDEIdInst, callback, APPCLASS_STANDARD, 0L);
+        if ( rc != DMLERR_NO_ERROR )
+        {
+            DDELogError(wxT("Failed to initialize DDE"), rc);
+        }
+        else
+        {
+            DDEInitialized = true;
+        }
+    }
+}
 
 void wxDDECleanUp()
 {
-  if (DDEIdInst != 0)
-  {
-    DdeUninitialize(DDEIdInst);
-    DDEIdInst = 0;
-  }
-  if (DDEDefaultIPCBuffer)
-    delete [] DDEDefaultIPCBuffer ;
+    // deleting them later won't work as DDE won't be initialized any more
+    wxASSERT_MSG( wxDDEServerObjects.empty() &&
+                    wxDDEClientObjects.empty(),
+                    wxT("all DDE objects should be deleted by now") );
+
+    wxAtomTable.clear();
+
+    if ( DDEIdInst != 0 )
+    {
+        DdeUninitialize(DDEIdInst);
+        DDEIdInst = 0;
+    }
 }
 
+// ----------------------------------------------------------------------------
+// functions working with the global connection list(s)
+// ----------------------------------------------------------------------------
+
 // Global find connection
 static wxDDEConnection *DDEFindConnection(HCONV hConv)
 {
-  wxNode *node = wxDDEServerObjects.First();
-  wxDDEConnection *found = NULL;
-  while (node && !found)
-  {
-    wxDDEServer *object = (wxDDEServer *)node->Data();
-    found = object->FindConnection((WXHCONV) hConv);
-    node = node->Next();
-  }
-  if (found)
-       return found;
-
-  node = wxDDEClientObjects.First();
-  while (node && !found)
-  {
-    wxDDEClient *object = (wxDDEClient *)node->Data();
-    found = object->FindConnection((WXHCONV) hConv);
-    node = node->Next();
-  }
-  return found;
+    wxDDEServerList::compatibility_iterator serverNode = wxDDEServerObjects.GetFirst();
+    wxDDEConnection *found = NULL;
+    while (serverNode && !found)
+    {
+        wxDDEServer *object = serverNode->GetData();
+        found = object->FindConnection((WXHCONV) hConv);
+        serverNode = serverNode->GetNext();
+    }
+
+    if (found)
+    {
+        return found;
+    }
+
+    wxDDEClientList::compatibility_iterator clientNode = wxDDEClientObjects.GetFirst();
+    while (clientNode && !found)
+    {
+        wxDDEClient *object = clientNode->GetData();
+        found = object->FindConnection((WXHCONV) hConv);
+        clientNode = clientNode->GetNext();
+    }
+    return found;
 }
 
 // Global delete connection
 static void DDEDeleteConnection(HCONV hConv)
 {
-  wxNode *node = wxDDEServerObjects.First();
-  bool found = FALSE;
-  while (node && !found)
-  {
-    wxDDEServer *object = (wxDDEServer *)node->Data();
-    found = object->DeleteConnection((WXHCONV) hConv);
-    node = node->Next();
-  }
-  if (found)
-       return;
-
-  node = wxDDEServerObjects.First();
-  while (node && !found)
-  {
-    wxDDEClient *object = (wxDDEClient *)node->Data();
-    found = object->DeleteConnection((WXHCONV) hConv);
-    node = node->Next();
-  }
+    wxDDEServerList::compatibility_iterator serverNode = wxDDEServerObjects.GetFirst();
+    bool found = false;
+    while (serverNode && !found)
+    {
+        wxDDEServer *object = serverNode->GetData();
+        found = object->DeleteConnection((WXHCONV) hConv);
+        serverNode = serverNode->GetNext();
+    }
+    if (found)
+    {
+        return;
+    }
+
+    wxDDEClientList::compatibility_iterator clientNode = wxDDEClientObjects.GetFirst();
+    while (clientNode && !found)
+    {
+        wxDDEClient *object = clientNode->GetData();
+        found = object->DeleteConnection((WXHCONV) hConv);
+        clientNode = clientNode->GetNext();
+    }
 }
 
 // Find a server from a service name
 static wxDDEServer *DDEFindServer(const wxString& s)
 {
-  wxNode *node = wxDDEServerObjects.First();
-  wxDDEServer *found = NULL;
-  while (node && !found)
-  {
-    wxDDEServer *object = (wxDDEServer *)node->Data();
-    
-    if (object->GetServiceName() == s)
-      found = object;
-    else node = node->Next();
-  }
-  return found;
+    wxDDEServerList::compatibility_iterator node = wxDDEServerObjects.GetFirst();
+    wxDDEServer *found = NULL;
+    while (node && !found)
+    {
+        wxDDEServer *object = node->GetData();
+
+        if (object->GetServiceName() == s)
+        {
+            found = object;
+        }
+        else
+        {
+            node = node->GetNext();
+        }
+    }
+
+    return found;
 }
 
-/*
- * Server
- *
- */
+// ----------------------------------------------------------------------------
+// wxDDEServer
+// ----------------------------------------------------------------------------
 
-wxDDEServer::wxDDEServer(void)
+wxDDEServer::wxDDEServer()
 {
-  m_serviceName = "";
-  wxDDEServerObjects.Append(this);
+    wxDDEInitialize();
+
+    wxDDEServerObjects.Append(this);
 }
 
-bool wxDDEServer::Create(const wxString& server_name)
+bool wxDDEServer::Create(const wxString& server)
 {
-  m_serviceName = server_name;
-  HSZ serviceName = DdeCreateStringHandle(DDEIdInst, (char*) (const char *)server_name, CP_WINANSI);
+    m_serviceName = server;
+
+    HSZ hsz = DDEAtomFromString(server);
+
+    if ( !hsz )
+    {
+        return false;
+    }
 
-  if (DdeNameService(DDEIdInst, serviceName, (HSZ) NULL, DNS_REGISTER) == 0)
-  {
-    DDEPrintError();
-    return FALSE;
-  }
-  return TRUE;
+
+    bool success = (DdeNameService(DDEIdInst, hsz, (HSZ) NULL, DNS_REGISTER)
+        != NULL);
+
+    if (!success)
+    {
+        DDELogError(wxString::Format(_("Failed to register DDE server '%s'"),
+            server.c_str()));
+    }
+
+    DDEFreeString(hsz);
+
+    return success;
 }
 
-wxDDEServer::~wxDDEServer(void)
+wxDDEServer::~wxDDEServer()
 {
-  if (m_serviceName != "")
-  {
-    HSZ serviceName = DdeCreateStringHandle(DDEIdInst, (char*) (const char *)m_serviceName, CP_WINANSI);
-    if (DdeNameService(DDEIdInst, serviceName, NULL, DNS_UNREGISTER) == 0)
+    if ( !m_serviceName.empty() )
     {
-      DDEPrintError();
+        HSZ hsz = DDEAtomFromString(m_serviceName);
+
+        if (hsz)
+        {
+            if ( !DdeNameService(DDEIdInst, hsz,
+                (HSZ) NULL, DNS_UNREGISTER) )
+            {
+                DDELogError(wxString::Format(
+                    _("Failed to unregister DDE server '%s'"),
+                    m_serviceName.c_str()));
+            }
+
+            DDEFreeString(hsz);
+        }
     }
-  }
-  wxDDEServerObjects.DeleteObject(this);
 
-  wxNode *node = m_connections.First();
-  while (node)
-  {
-    wxDDEConnection *connection = (wxDDEConnection *)node->Data();
-    wxNode *next = node->Next();
-    connection->OnDisconnect(); // May delete the node implicitly
-    node = next;
-  }
+    wxDDEServerObjects.DeleteObject(this);
 
-  // If any left after this, delete them
-  node = m_connections.First();
-  while (node)
-  {
-    wxDDEConnection *connection = (wxDDEConnection *)node->Data();
-    wxNode *next = node->Next();
-    delete connection;
-    node = next;
-  }
+    wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
+    while (node)
+    {
+        wxDDEConnection *connection = node->GetData();
+        wxDDEConnectionList::compatibility_iterator next = node->GetNext();
+        connection->SetConnected(false);
+        connection->OnDisconnect(); // May delete the node implicitly
+        node = next;
+    }
+
+    // If any left after this, delete them
+    node = m_connections.GetFirst();
+    while (node)
+    {
+        wxDDEConnection *connection = node->GetData();
+        wxDDEConnectionList::compatibility_iterator next = node->GetNext();
+        delete connection;
+        node = next;
+    }
 }
 
 wxConnectionBase *wxDDEServer::OnAcceptConnection(const wxString& /* topic */)
 {
-       return new wxDDEConnection;
+    return new wxDDEConnection;
 }
 
 wxDDEConnection *wxDDEServer::FindConnection(WXHCONV conv)
 {
-  wxNode *node = m_connections.First();
-  wxDDEConnection *found = NULL;
-  while (node && !found)
-  {
-    wxDDEConnection *connection = (wxDDEConnection *)node->Data();
-    if (connection->m_hConv == conv)
-      found = connection;
-    else node = node->Next();
-  }
-  return found;
+    wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
+    wxDDEConnection *found = NULL;
+    while (node && !found)
+    {
+        wxDDEConnection *connection = node->GetData();
+        if (connection->m_hConv == conv)
+            found = connection;
+        else node = node->GetNext();
+    }
+    return found;
 }
 
 // Only delete the entry in the map, not the actual connection
 bool wxDDEServer::DeleteConnection(WXHCONV conv)
 {
-  wxNode *node = m_connections.First();
-  bool found = FALSE;
-  while (node && !found)
-  {
-    wxDDEConnection *connection = (wxDDEConnection *)node->Data();
-    if (connection->m_hConv == conv)
+    wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
+    while (node)
     {
-      found = TRUE;
-      delete node;
+        wxDDEConnection *connection = node->GetData();
+        if (connection->m_hConv == conv)
+        {
+            m_connections.Erase(node);
+            return true;
+        }
+        else
+        {
+            node = node->GetNext();
+        }
     }
-    else node = node->Next();
-  }
-  return found;
+    return false;
 }
 
+// ----------------------------------------------------------------------------
+// wxDDEClient
+// ----------------------------------------------------------------------------
 
-/*
- * Client
- *
- */
-
-
-wxDDEClient::wxDDEClient(void)
+wxDDEClient::wxDDEClient()
 {
-  wxDDEClientObjects.Append(this);
+    wxDDEInitialize();
+
+    wxDDEClientObjects.Append(this);
 }
 
-wxDDEClient::~wxDDEClient(void)
+wxDDEClient::~wxDDEClient()
 {
-  wxDDEClientObjects.DeleteObject(this);
-  wxNode *node = m_connections.First();
-  while (node)
-  {
-    wxDDEConnection *connection = (wxDDEConnection *)node->Data();
-    delete connection;  // Deletes the node implicitly (see ~wxDDEConnection)
-    node = m_connections.First();
-  }
+    wxDDEClientObjects.DeleteObject(this);
+    wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
+    while (node)
+    {
+        wxDDEConnection *connection = node->GetData();
+        delete connection;  // Deletes the node implicitly (see ~wxDDEConnection)
+        node = m_connections.GetFirst();
+    }
 }
 
 bool wxDDEClient::ValidHost(const wxString& /* host */)
 {
-  return TRUE;
+    return true;
 }
 
-wxConnectionBase *wxDDEClient::MakeConnection(const wxString& /* host */, const wxString& server_name, const wxString& topic)
+wxConnectionBase *wxDDEClient::MakeConnection(const wxString& WXUNUSED(host),
+                                              const wxString& server,
+                                              const wxString& topic)
 {
-  HSZ serviceName = DdeCreateStringHandle(DDEIdInst, (char*) (const char *)server_name, CP_WINANSI);
-  HSZ topic_atom = DdeCreateStringHandle(DDEIdInst, (char*) (const char *)topic, CP_WINANSI);
+    HSZ hszServer = DDEAtomFromString(server);
+
+    if ( !hszServer )
+    {
+        return NULL;
+    }
+
+
+    HSZ hszTopic = DDEAtomFromString(topic);
+
+    if ( !hszTopic )
+    {
+        DDEFreeString(hszServer);
+        return NULL;
+    }
 
-  HCONV hConv = DdeConnect(DDEIdInst, serviceName, topic_atom, (PCONVCONTEXT)NULL);
-  if (hConv == (HCONV) NULL)
-    return (wxConnectionBase*) NULL;
-  else
-  {
-    wxDDEConnection *connection = (wxDDEConnection*) OnMakeConnection();
-    if (connection)
+
+    HCONV hConv = ::DdeConnect(DDEIdInst, hszServer, hszTopic,
+        (PCONVCONTEXT) NULL);
+
+    DDEFreeString(hszServer);
+    DDEFreeString(hszTopic);
+
+
+    if ( !hConv )
     {
-      connection->m_hConv = (WXHCONV) hConv;
-      connection->m_topicName = topic;
-         connection->m_client = this;
-      m_connections.Append(connection);
-      return connection;
+        DDELogError( wxString::Format(
+            _("Failed to create connection to server '%s' on topic '%s'"),
+            server.c_str(), topic.c_str()) );
+    }
+    else
+    {
+        wxDDEConnection *connection = (wxDDEConnection*) OnMakeConnection();
+        if (connection)
+        {
+            connection->m_hConv = (WXHCONV) hConv;
+            connection->m_topicName = topic;
+            connection->m_client = this;
+            m_connections.Append(connection);
+            return connection;
+        }
     }
-    else return (wxConnectionBase*) NULL;
-  }
+
+    return NULL;
 }
 
-wxConnectionBase *wxDDEClient::OnMakeConnection(void)
+wxConnectionBase *wxDDEClient::OnMakeConnection()
 {
-       return new wxDDEConnection;
+    return new wxDDEConnection;
 }
 
 wxDDEConnection *wxDDEClient::FindConnection(WXHCONV conv)
 {
-  wxNode *node = m_connections.First();
-  wxDDEConnection *found = NULL;
-  while (node && !found)
-  {
-    wxDDEConnection *connection = (wxDDEConnection *)node->Data();
-    if (connection->m_hConv == conv)
-      found = connection;
-    else node = node->Next();
-  }
-  return found;
+    wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
+    wxDDEConnection *found = NULL;
+    while (node && !found)
+    {
+        wxDDEConnection *connection = node->GetData();
+        if (connection->m_hConv == conv)
+            found = connection;
+        else node = node->GetNext();
+    }
+    return found;
 }
 
 // Only delete the entry in the map, not the actual connection
 bool wxDDEClient::DeleteConnection(WXHCONV conv)
 {
-  wxNode *node = m_connections.First();
-  bool found = FALSE;
-  while (node && !found)
-  {
-    wxDDEConnection *connection = (wxDDEConnection *)node->Data();
-    if (connection->m_hConv == conv)
+    wxDDEConnectionList::compatibility_iterator node = m_connections.GetFirst();
+    while (node)
     {
-      found = TRUE;
-      delete node;
+        wxDDEConnection *connection = node->GetData();
+        if (connection->m_hConv == conv)
+        {
+            m_connections.Erase(node);
+            return true;
+        }
+        else node = node->GetNext();
     }
-    else node = node->Next();
-  }
-  return found;
+    return false;
 }
 
-/*
- * Connection
- */
+// ----------------------------------------------------------------------------
+// wxDDEConnection
+// ----------------------------------------------------------------------------
 
-wxDDEConnection::wxDDEConnection(char *buffer, int size)
+wxDDEConnection::wxDDEConnection(void *buffer, size_t size)
+     : wxConnectionBase(buffer, size)
 {
-  if (buffer == NULL)
-  {
-    if (DDEDefaultIPCBuffer == NULL)
-      DDEDefaultIPCBuffer = new char[DDEDefaultIPCBufferSize];
-    m_bufPtr = DDEDefaultIPCBuffer;
-    m_bufSize = DDEDefaultIPCBufferSize;
-  }
-  else
-  {
-    m_bufPtr = buffer;
-    m_bufSize = size;
-  }
+    m_client = NULL;
+    m_server = NULL;
 
-  m_topicName = "";
-
-  m_client = NULL;
-  m_server = NULL;
-
-  m_hConv = 0;
-  m_sendingData = NULL;
+    m_hConv = 0;
+    m_sendingData = NULL;
 }
 
-wxDDEConnection::wxDDEConnection(void)
+wxDDEConnection::wxDDEConnection()
+     : wxConnectionBase()
 {
-  m_hConv = 0;
-  m_sendingData = NULL;
-  m_server = NULL;
-  m_client = NULL;
-  if (DDEDefaultIPCBuffer == NULL)
-    DDEDefaultIPCBuffer = new char[DDEDefaultIPCBufferSize];
-
-  m_bufPtr = DDEDefaultIPCBuffer;
-  m_bufSize = DDEDefaultIPCBufferSize;
-  m_topicName = "";
+    m_hConv = 0;
+    m_sendingData = NULL;
+    m_server = NULL;
+    m_client = NULL;
 }
 
-wxDDEConnection::~wxDDEConnection(void)
+wxDDEConnection::~wxDDEConnection()
 {
-       if (m_server)
-               m_server->GetConnections().DeleteObject(this);
-       else
-           m_client->GetConnections().DeleteObject(this);
+    Disconnect();
+    if (m_server)
+        m_server->GetConnections().DeleteObject(this);
+    else
+        m_client->GetConnections().DeleteObject(this);
 }
 
 // Calls that CLIENT can make
-bool wxDDEConnection::Disconnect(void)
+bool wxDDEConnection::Disconnect()
 {
-  DDEDeleteConnection((HCONV) m_hConv);
-  return (DdeDisconnect((HCONV) m_hConv) != 0);
+    if ( !GetConnected() )
+        return true;
+
+    DDEDeleteConnection(GetHConv());
+
+    bool ok = DdeDisconnect(GetHConv()) != 0;
+    if ( !ok )
+    {
+        DDELogError(wxT("Failed to disconnect from DDE server gracefully"));
+    }
+
+    SetConnected( false );  // so we don't try and disconnect again
+
+    return ok;
 }
 
-bool wxDDEConnection::Execute(char *data, int size, wxIPCFormat format)
+bool
+wxDDEConnection::DoExecute(const void *data, size_t size, wxIPCFormat format)
 {
-  DWORD result;
-  if (size < 0)
-    size = strlen(data);
+    wxCHECK_MSG( format == wxIPC_TEXT ||
+                 format == wxIPC_UTF8TEXT ||
+                 format == wxIPC_UNICODETEXT,
+                 false,
+                 wxT("wxDDEServer::Execute() supports only text data") );
+
+    wxMemoryBuffer buffer;
+    LPBYTE realData = NULL;
+    size_t realSize = 0;
+    wxMBConv *conv = NULL;
+
+    // Windows only supports either ANSI or UTF-16 format depending on the
+    // build, so we need to convert the data if it doesn't use it already
+#if wxUSE_UNICODE
+    if ( format == wxIPC_TEXT )
+    {
+        conv = &wxConvLibc;
+    }
+    else if ( format == wxIPC_UTF8TEXT )
+    {
+        conv = &wxConvUTF8;
+    }
+    else // no conversion necessary for wxIPC_UNICODETEXT
+    {
+        realData = (LPBYTE)data;
+        realSize = size;
+    }
+
+    if ( conv )
+    {
+        const char * const text = (const char *)data;
+        const size_t len = size/sizeof(char);
+
+        realSize = conv->ToWChar(NULL, 0, text, len);
+        if ( realSize == wxCONV_FAILED )
+            return false;
+
+        realData = (LPBYTE)buffer.GetWriteBuf(realSize*sizeof(wchar_t));
+        if ( !realData )
+            return false;
+
+        realSize = conv->ToWChar((wchar_t *)realData, realSize, text, len);
+        if ( realSize == wxCONV_FAILED )
+            return false;
+    }
+#else // !wxUSE_UNICODE
+    if ( format == wxIPC_UNICODETEXT )
+    {
+        conv = &wxConvLibc;
+    }
+    else if ( format == wxIPC_UTF8TEXT )
+    {
+        // we could implement this in theory but it's not obvious how to pass
+        // the format information and, basically, why bother -- just use
+        // Unicode build
+        wxFAIL_MSG( wxT("UTF-8 text not supported in ANSI build") );
 
-  size ++;
+        return false;
+    }
+    else // don't convert wxIPC_TEXT
+    {
+        realData = (LPBYTE)data;
+        realSize = size;
+    }
 
-  return (DdeClientTransaction((LPBYTE)data, size, (HCONV) m_hConv,
-    NULL, format, XTYP_EXECUTE, 5000, &result) ? TRUE : FALSE);
+    if ( conv )
+    {
+        const wchar_t * const wtext = (const wchar_t *)data;
+        const size_t len = size/sizeof(wchar_t);
+
+        realSize = conv->FromWChar(NULL, 0, wtext, len);
+        if ( realSize == wxCONV_FAILED )
+            return false;
+
+        realData = (LPBYTE)buffer.GetWriteBuf(realSize*sizeof(char));
+        if ( !realData )
+            return false;
+
+        realSize = conv->FromWChar((char*)realData, realSize, wtext, len);
+        if ( realSize == wxCONV_FAILED )
+            return false;
+    }
+#endif // wxUSE_UNICODE/!wxUSE_UNICODE
+
+    DWORD result;
+    bool ok = DdeClientTransaction(realData,
+                                   realSize*sizeof(wxChar),
+                                   GetHConv(),
+                                   NULL,
+                                   // MSDN: if the transaction specified by
+                                   // the wType parameter does not pass data
+                                   // or is XTYP_EXECUTE, wFmt should be zero.
+                                   0,
+                                   XTYP_EXECUTE,
+                                   DDE_TIMEOUT,
+                                   &result) != 0;
+
+    if ( !ok )
+    {
+        DDELogError(wxT("DDE execute request failed"));
+    }
+
+    return ok;
 }
 
-char *wxDDEConnection::Request(const wxString& item, int *size, wxIPCFormat format)
+const void *wxDDEConnection::Request(const wxString& item, size_t *size, wxIPCFormat format)
 {
-  DWORD result;
-  HSZ atom = DDEGetAtom(item);
+    DWORD result;
+
+    HSZ atom = DDEGetAtom(item);
+
+    HDDEDATA returned_data = DdeClientTransaction(NULL, 0,
+                                                  GetHConv(),
+                                                  atom, format,
+                                                  XTYP_REQUEST,
+                                                  DDE_TIMEOUT,
+                                                  &result);
+    if ( !returned_data )
+    {
+        DDELogError(wxT("DDE data request failed"));
+
+        return NULL;
+    }
+
+    DWORD len = DdeGetData(returned_data, NULL, 0, 0);
 
-  HDDEDATA returned_data = DdeClientTransaction(NULL, 0, (HCONV) m_hConv,
-    atom, format, XTYP_REQUEST, 5000, &result);
+    void *data = GetBufferAtLeast(len);
+    wxASSERT_MSG(data != NULL,
+                 wxT("Buffer too small in wxDDEConnection::Request") );
+    (void) DdeGetData(returned_data, (LPBYTE)data, len, 0);
 
-  DWORD len = DdeGetData(returned_data, (LPBYTE)(m_bufPtr), m_bufSize, 0);
+    (void) DdeFreeDataHandle(returned_data);
 
-  DdeFreeDataHandle(returned_data);
+    if (size)
+        *size = (size_t)len;
 
-  if (size) *size = (int)len;
-  if (len > 0)
-  {
-    return m_bufPtr;
-  }
-  else return NULL;
+    return data;
 }
 
-bool wxDDEConnection::Poke(const wxString& item, char *data, int size, wxIPCFormat format)
+bool wxDDEConnection::DoPoke(const wxString& item, const void *data, size_t size, wxIPCFormat format)
 {
-  DWORD result;
-  if (size < 0)
-    size = strlen(data);
-
-  size ++;
+    DWORD result;
+
+    HSZ item_atom = DDEGetAtom(item);
+    bool ok = DdeClientTransaction((LPBYTE)data,
+                                   size,
+                                   GetHConv(),
+                                   item_atom, format,
+                                   XTYP_POKE,
+                                   DDE_TIMEOUT,
+                                   &result) != 0;
+    if ( !ok )
+    {
+        DDELogError(_("DDE poke request failed"));
+    }
 
-  HSZ item_atom = DDEGetAtom(item);
-  return (DdeClientTransaction((LPBYTE)data, size, (HCONV) m_hConv,
-    item_atom, format, XTYP_POKE, 5000, &result) ? TRUE : FALSE);
+    return ok;
 }
 
 bool wxDDEConnection::StartAdvise(const wxString& item)
 {
-  DWORD result;
-  HSZ atom = DDEGetAtom(item);
+    DWORD result;
+    HSZ atom = DDEGetAtom(item);
+
+    bool ok = DdeClientTransaction(NULL, 0,
+                                   GetHConv(),
+                                   atom, CF_TEXT,
+                                   XTYP_ADVSTART,
+                                   DDE_TIMEOUT,
+                                   &result) != 0;
+    if ( !ok )
+    {
+        DDELogError(_("Failed to establish an advise loop with DDE server"));
+    }
 
-  return (DdeClientTransaction(NULL, 0, (HCONV) m_hConv,
-    atom, CF_TEXT, XTYP_ADVSTART, 5000, &result) ? TRUE : FALSE);
+    return ok;
 }
 
 bool wxDDEConnection::StopAdvise(const wxString& item)
 {
-  DWORD result;
-  HSZ atom = DDEGetAtom(item);
+    DWORD result;
+    HSZ atom = DDEGetAtom(item);
+
+    bool ok = DdeClientTransaction(NULL, 0,
+                                   GetHConv(),
+                                   atom, CF_TEXT,
+                                   XTYP_ADVSTOP,
+                                   DDE_TIMEOUT,
+                                   &result) != 0;
+    if ( !ok )
+    {
+        DDELogError(_("Failed to terminate the advise loop with DDE server"));
+    }
 
-  return (DdeClientTransaction(NULL, 0, (HCONV) m_hConv,
-    atom, CF_TEXT, XTYP_ADVSTOP, 5000, &result) ? TRUE : FALSE);
+    return ok;
 }
 
 // Calls that SERVER can make
-bool wxDDEConnection::Advise(const wxString& item, char *data, int size, wxIPCFormat format)
+bool wxDDEConnection::DoAdvise(const wxString& item,
+                               const void *data,
+                               size_t size,
+                               wxIPCFormat format)
 {
-  if (size < 0)
-    size = strlen(data);
-
-  size ++;
-
-  HSZ item_atom = DDEGetAtom(item);
-  HSZ topic_atom = DDEGetAtom(m_topicName);
-  m_sendingData = data;
-  m_dataSize = size;
-  m_dataType = format;
-  return (DdePostAdvise(DDEIdInst, topic_atom, item_atom) != 0);
-}
+    HSZ item_atom = DDEGetAtom(item);
+    HSZ topic_atom = DDEGetAtom(m_topicName);
+    m_sendingData = data;  // mrf: potential for scope problems here?
+    m_dataSize = size;
+    // wxIPC_PRIVATE does not succeed, so use text instead
+    m_dataType = format == wxIPC_PRIVATE ? wxIPC_TEXT : format;
+
+    bool ok = DdePostAdvise(DDEIdInst, topic_atom, item_atom) != 0;
+    if ( !ok )
+    {
+        DDELogError(_("Failed to send DDE advise notification"));
+    }
 
-bool wxDDEConnection::OnDisconnect(void)
-{
-       delete this;
-       return TRUE;
+    return ok;
 }
 
+// ----------------------------------------------------------------------------
+// _DDECallback
+// ----------------------------------------------------------------------------
 
 #define DDERETURN HDDEDATA
 
-HDDEDATA EXPENTRY _EXPORT _DDECallback(
-WORD wType,
-WORD wFmt,
-HCONV hConv,
-HSZ hsz1,
-HSZ hsz2,
-HDDEDATA hData,
-DWORD /* lData1 */,
-DWORD /* lData2 */)
-{
-  switch (wType)
-  {
-    case XTYP_CONNECT:
-    {
-      char topic_buf[100];
-      char server_buf[100];
-      DdeQueryString(DDEIdInst, hsz1, (LPSTR)topic_buf, sizeof(topic_buf),
-                     CP_WINANSI);
-      DdeQueryString(DDEIdInst, hsz2, (LPSTR)server_buf, sizeof(topic_buf),
-                     CP_WINANSI);
-      wxDDEServer *server = DDEFindServer(server_buf);
-      if (server)
-      {
-        wxDDEConnection *connection =
-          (wxDDEConnection*) server->OnAcceptConnection(wxString(topic_buf));
-        if (connection)
-        {
-          connection->m_server = server;
-          server->GetConnections().Append(connection);
-          connection->m_hConv = 0;
-          connection->m_topicName = topic_buf;
-          DDECurrentlyConnecting = connection;
-          return (DDERETURN)TRUE;
-        }
-      }
-      else return (DDERETURN)0;
-      break;
-    }
-
-    case XTYP_CONNECT_CONFIRM:
+HDDEDATA EXPENTRY _EXPORT
+_DDECallback(WORD wType,
+             WORD wFmt,
+             HCONV hConv,
+             HSZ hsz1,
+             HSZ hsz2,
+             HDDEDATA hData,
+             DWORD WXUNUSED(lData1),
+             DWORD WXUNUSED(lData2))
+{
+    switch (wType)
     {
-      if (DDECurrentlyConnecting)
-      {
-        DDECurrentlyConnecting->m_hConv = (WXHCONV) hConv;
-        DDECurrentlyConnecting = NULL;
-        return (DDERETURN)TRUE;
-      }
-      else return 0;
-      break;
+        case XTYP_CONNECT:
+            {
+                wxString topic = DDEStringFromAtom(hsz1),
+                         srv = DDEStringFromAtom(hsz2);
+                wxDDEServer *server = DDEFindServer(srv);
+                if (server)
+                {
+                    wxDDEConnection *connection =
+                        (wxDDEConnection*) server->OnAcceptConnection(topic);
+                    if (connection)
+                    {
+                        connection->m_server = server;
+                        server->GetConnections().Append(connection);
+                        connection->m_hConv = 0;
+                        connection->m_topicName = topic;
+                        DDECurrentlyConnecting = connection;
+                        return (DDERETURN)(DWORD)true;
+                    }
+                }
+                break;
+            }
+
+        case XTYP_CONNECT_CONFIRM:
+            {
+                if (DDECurrentlyConnecting)
+                {
+                    DDECurrentlyConnecting->m_hConv = (WXHCONV) hConv;
+                    DDECurrentlyConnecting = NULL;
+                    return (DDERETURN)(DWORD)true;
+                }
+                break;
+            }
+
+        case XTYP_DISCONNECT:
+            {
+                wxDDEConnection *connection = DDEFindConnection(hConv);
+                if (connection)
+                {
+                    connection->SetConnected( false );
+                    if (connection->OnDisconnect())
+                    {
+                        DDEDeleteConnection(hConv);  // Delete mapping: hConv => connection
+                        return (DDERETURN)(DWORD)true;
+                    }
+                }
+                break;
+            }
+
+        case XTYP_EXECUTE:
+            {
+                wxDDEConnection *connection = DDEFindConnection(hConv);
+
+                if (connection)
+                {
+                    DWORD len = DdeGetData(hData, NULL, 0, 0);
+
+                    void *data = connection->GetBufferAtLeast(len);
+                    wxASSERT_MSG(data != NULL,
+                                 wxT("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
+
+                    DdeGetData(hData, (LPBYTE)data, len, 0);
+
+                    DdeFreeDataHandle(hData);
+
+                    // XTYP_EXECUTE can be used for text only and the text is
+                    // always in ANSI format for ANSI build and Unicode format
+                    // in Unicode build
+                    #if wxUSE_UNICODE
+                        wFmt = wxIPC_UNICODETEXT;
+                    #else
+                        wFmt = wxIPC_TEXT;
+                    #endif
+
+                    if ( connection->OnExecute(connection->m_topicName,
+                                               data,
+                                               (int)len,
+                                               (wxIPCFormat)wFmt) )
+                    {
+                        return (DDERETURN)(DWORD)DDE_FACK;
+                    }
+                }
+
+                return (DDERETURN)DDE_FNOTPROCESSED;
+            }
+
+        case XTYP_REQUEST:
+            {
+                wxDDEConnection *connection = DDEFindConnection(hConv);
+
+                if (connection)
+                {
+                    wxString item_name = DDEStringFromAtom(hsz2);
+
+                    size_t user_size = wxNO_LEN;
+                    const void *data = connection->OnRequest(connection->m_topicName,
+                                                             item_name,
+                                                             &user_size,
+                                                             (wxIPCFormat)wFmt);
+                    if (data)
+                    {
+                      if (user_size == wxNO_LEN)
+                        switch (wFmt)
+                        {
+                          case wxIPC_TEXT:
+                          case wxIPC_UTF8TEXT:
+                            user_size = strlen((const char*)data) + 1;  // includes final NUL
+                            break;
+                          case wxIPC_UNICODETEXT:
+                            user_size = (wcslen((const wchar_t*)data) + 1) * sizeof(wchar_t);  // includes final NUL
+                            break;
+                          default:
+                            user_size = 0;
+                        }
+
+                        HDDEDATA handle = DdeCreateDataHandle(DDEIdInst,
+                                                              (LPBYTE)data,
+                                                              user_size,
+                                                              0,
+                                                              hsz2,
+                                                              wFmt,
+                                                              0);
+                        return (DDERETURN)handle;
+                    }
+                }
+                break;
+            }
+
+        case XTYP_POKE:
+            {
+                wxDDEConnection *connection = DDEFindConnection(hConv);
+
+                if (connection)
+                {
+                    wxString item_name = DDEStringFromAtom(hsz2);
+
+                    DWORD len = DdeGetData(hData, NULL, 0, 0);
+
+                    void *data = connection->GetBufferAtLeast(len);
+                    wxASSERT_MSG(data != NULL,
+                                 wxT("Buffer too small in _DDECallback (XTYP_POKE)") );
+
+                    DdeGetData(hData, (LPBYTE)data, len, 0);
+
+                    DdeFreeDataHandle(hData);
+
+                    connection->OnPoke(connection->m_topicName,
+                                       item_name,
+                                       data,
+                                       (int)len,
+                                       (wxIPCFormat) wFmt);
+
+                    return (DDERETURN)DDE_FACK;
+                }
+                else
+                {
+                    return (DDERETURN)DDE_FNOTPROCESSED;
+                }
+            }
+
+        case XTYP_ADVSTART:
+            {
+                wxDDEConnection *connection = DDEFindConnection(hConv);
+
+                if (connection)
+                {
+                    wxString item_name = DDEStringFromAtom(hsz2);
+
+                    return (DDERETURN)connection->
+                                OnStartAdvise(connection->m_topicName, item_name);
+                }
+
+                break;
+            }
+
+        case XTYP_ADVSTOP:
+            {
+                wxDDEConnection *connection = DDEFindConnection(hConv);
+
+                if (connection)
+                {
+                    wxString item_name = DDEStringFromAtom(hsz2);
+
+                    return (DDERETURN)connection->
+                        OnStopAdvise(connection->m_topicName, item_name);
+                }
+
+                break;
+            }
+
+        case XTYP_ADVREQ:
+            {
+                wxDDEConnection *connection = DDEFindConnection(hConv);
+
+                if (connection && connection->m_sendingData)
+                {
+                    HDDEDATA data = DdeCreateDataHandle
+                                    (
+                                        DDEIdInst,
+                                        (LPBYTE)connection->m_sendingData,
+                                        connection->m_dataSize,
+                                        0,
+                                        hsz2,
+                                        connection->m_dataType,
+                                        0
+                                    );
+
+                    connection->m_sendingData = NULL;
+
+                    return (DDERETURN)data;
+                }
+
+                break;
+            }
+
+        case XTYP_ADVDATA:
+            {
+                wxDDEConnection *connection = DDEFindConnection(hConv);
+
+                if (connection)
+                {
+                    wxString item_name = DDEStringFromAtom(hsz2);
+
+                    DWORD len = DdeGetData(hData, NULL, 0, 0);
+
+                    void *data = connection->GetBufferAtLeast(len);
+                    wxASSERT_MSG(data != NULL,
+                                 wxT("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
+
+                    DdeGetData(hData, (LPBYTE)data, len, 0);
+
+                    DdeFreeDataHandle(hData);
+                    if ( connection->OnAdvise(connection->m_topicName,
+                                              item_name,
+                                              data,
+                                              (int)len,
+                                              (wxIPCFormat) wFmt) )
+                    {
+                        return (DDERETURN)(DWORD)DDE_FACK;
+                    }
+                }
+
+                return (DDERETURN)DDE_FNOTPROCESSED;
+            }
     }
 
-    case XTYP_DISCONNECT:
-    {
-      wxDDEConnection *connection = DDEFindConnection(hConv);
-      if (connection && connection->OnDisconnect())
-      {
-        DDEDeleteConnection(hConv);  // Delete mapping: hConv => connection
-        return (DDERETURN)TRUE;
-      }
-      else return (DDERETURN)0;
-      break;
-    }
+    return (DDERETURN)0;
+}
 
-    case XTYP_EXECUTE:
-    {
-      wxDDEConnection *connection = DDEFindConnection(hConv);
+// ----------------------------------------------------------------------------
+// DDE strings and atoms
+// ----------------------------------------------------------------------------
 
-      if (connection)
-      {
-        DWORD len = DdeGetData(hData, (LPBYTE)(connection->m_bufPtr), connection->m_bufSize, 0);
-        DdeFreeDataHandle(hData);
-        if (connection->OnExecute(connection->m_topicName, connection->m_bufPtr, (int)len, (wxIPCFormat) wFmt))
-          return (DDERETURN)DDE_FACK;
-        else
-          return (DDERETURN)DDE_FNOTPROCESSED;
-      } else return (DDERETURN)DDE_FNOTPROCESSED;
-      break;
-    }
+// Atom table stuff
+static HSZ DDEAddAtom(const wxString& str)
+{
+    HSZ atom = DDEAtomFromString(str);
+    wxAtomTable[str] = atom;
+    return atom;
+}
 
-    case XTYP_REQUEST:
-    {
-      wxDDEConnection *connection = DDEFindConnection(hConv);
+static HSZ DDEGetAtom(const wxString& str)
+{
+    wxAtomMap::iterator it = wxAtomTable.find(str);
 
-      if (connection)
-      {
-        char item_name[200];
-        DdeQueryString(DDEIdInst, hsz2, (LPSTR)item_name, sizeof(item_name),
-                     CP_WINANSI);
+    if (it != wxAtomTable.end())
+        return it->second;
 
-        int user_size = -1;
-        char *data = connection->OnRequest(connection->m_topicName, wxString(item_name), &user_size, (wxIPCFormat) wFmt);
-        if (data)
-        {
-          if (user_size < 0) user_size = strlen(data);
+    return DDEAddAtom(str);
+}
 
-          HDDEDATA handle = DdeCreateDataHandle(DDEIdInst,
-                 (LPBYTE)data, user_size + 1, 0, hsz2, wFmt, 0);
-          return (DDERETURN)handle;
-        } else return (DDERETURN)0;
-      } else return (DDERETURN)0;
-      break;
-    }
+/* atom <-> strings
+The returned handle has to be freed by the caller (using
+(static) DDEFreeString).
+*/
+static HSZ DDEAtomFromString(const wxString& s)
+{
+    wxASSERT_MSG( DDEIdInst, wxT("DDE not initialized") );
 
-    case XTYP_POKE:
+    HSZ hsz = DdeCreateStringHandle(DDEIdInst, (wxChar*)s.wx_str(), DDE_CP);
+    if ( !hsz )
     {
-      wxDDEConnection *connection = DDEFindConnection(hConv);
-
-      if (connection)
-      {
-        char item_name[200];
-        DdeQueryString(DDEIdInst, hsz2, (LPSTR)item_name, sizeof(item_name),
-                     CP_WINANSI);
-        DWORD len = DdeGetData(hData, (LPBYTE)(connection->m_bufPtr), connection->m_bufSize, 0);
-        DdeFreeDataHandle(hData);
-        connection->OnPoke(connection->m_topicName, wxString(item_name), connection->m_bufPtr, (int)len, (wxIPCFormat) wFmt);
-        return (DDERETURN)DDE_FACK;
-      } else return (DDERETURN)DDE_FNOTPROCESSED;
-      break;
+        DDELogError(_("Failed to create DDE string"));
     }
 
-    case XTYP_ADVSTART:
-    {
-      wxDDEConnection *connection = DDEFindConnection(hConv);
-
-      if (connection)
-      {
-        char item_name[200];
-        DdeQueryString(DDEIdInst, hsz2, (LPSTR)item_name, sizeof(item_name),
-                     CP_WINANSI);
+    return hsz;
+}
 
-        return (DDERETURN)connection->OnStartAdvise(connection->m_topicName, wxString(item_name));
-      } else return (DDERETURN)0;
-      break;
-    }
+static wxString DDEStringFromAtom(HSZ hsz)
+{
+    // all DDE strings are normally limited to 255 bytes
+    static const size_t len = 256;
 
-    case XTYP_ADVSTOP:
-    {
-      wxDDEConnection *connection = DDEFindConnection(hConv);
+    wxString s;
+    (void)DdeQueryString(DDEIdInst, hsz, wxStringBuffer(s, len), len, DDE_CP);
 
-      if (connection)
-      {
-        char item_name[200];
-        DdeQueryString(DDEIdInst, hsz2, (LPSTR)item_name, sizeof(item_name),
-                     CP_WINANSI);
-        return (DDERETURN)connection->OnStopAdvise(connection->m_topicName, wxString(item_name));
-      } else return (DDERETURN)0;
-      break;
-    }
+    return s;
+}
 
-    case XTYP_ADVREQ:
-    {
-      wxDDEConnection *connection = DDEFindConnection(hConv);
+static void DDEFreeString(HSZ hsz)
+{
+    // DS: Failure to free a string handle might indicate there's
+    // some other severe error.
+    bool ok = (::DdeFreeStringHandle(DDEIdInst, hsz) != 0);
+    wxASSERT_MSG( ok, wxT("Failed to free DDE string handle") );
+    wxUnusedVar(ok);
+}
 
-      if (connection && connection->m_sendingData)
-      {
-        HDDEDATA data = DdeCreateDataHandle(DDEIdInst,
-                          (LPBYTE)connection->m_sendingData,
-                          connection->m_dataSize, 0, hsz2, connection->m_dataType, 0);
-        connection->m_sendingData = NULL;
-        return (DDERETURN)data;
-      } else return (DDERETURN)NULL;
-      break;
-    }
+// ----------------------------------------------------------------------------
+// error handling
+// ----------------------------------------------------------------------------
 
-    case XTYP_ADVDATA:
+static void DDELogError(const wxString& s, UINT error)
+{
+    if ( !error )
     {
-      wxDDEConnection *connection = DDEFindConnection(hConv);
+        error = DdeGetLastError(DDEIdInst);
+    }
 
-      if (connection)
-      {
-        char item_name[200];
-        DdeQueryString(DDEIdInst, hsz2, (LPSTR)item_name, sizeof(item_name),
-                     CP_WINANSI);
+    wxLogError(s + wxT(": ") + DDEGetErrorMsg(error));
+}
 
-        DWORD len = DdeGetData(hData, (LPBYTE)(connection->m_bufPtr), connection->m_bufSize, 0);
-        DdeFreeDataHandle(hData);
-        if (connection->OnAdvise(connection->m_topicName, wxString(item_name), connection->m_bufPtr, (int)len, (wxIPCFormat) wFmt))
-          return (DDERETURN)DDE_FACK;
-        else
-          return (DDERETURN)DDE_FNOTPROCESSED;
-      } else return (DDERETURN)DDE_FNOTPROCESSED;
-      break;
+static wxString DDEGetErrorMsg(UINT error)
+{
+    wxString err;
+    switch ( error )
+    {
+        case DMLERR_NO_ERROR:
+            err = _("no DDE error.");
+            break;
+
+        case DMLERR_ADVACKTIMEOUT:
+            err = _("a request for a synchronous advise transaction has timed out.");
+            break;
+        case DMLERR_BUSY:
+            err = _("the response to the transaction caused the DDE_FBUSY bit to be set.");
+            break;
+        case DMLERR_DATAACKTIMEOUT:
+            err = _("a request for a synchronous data transaction has timed out.");
+            break;
+        case DMLERR_DLL_NOT_INITIALIZED:
+            err = _("a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.");
+            break;
+        case DMLERR_DLL_USAGE:
+            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.");
+            break;
+        case DMLERR_EXECACKTIMEOUT:
+            err = _("a request for a synchronous execute transaction has timed out.");
+            break;
+        case DMLERR_INVALIDPARAMETER:
+            err = _("a parameter failed to be validated by the DDEML.");
+            break;
+        case DMLERR_LOW_MEMORY:
+            err = _("a DDEML application has created a prolonged race condition.");
+            break;
+        case DMLERR_MEMORY_ERROR:
+            err = _("a memory allocation failed.");
+            break;
+        case DMLERR_NO_CONV_ESTABLISHED:
+            err = _("a client's attempt to establish a conversation has failed.");
+            break;
+        case DMLERR_NOTPROCESSED:
+            err = _("a transaction failed.");
+            break;
+        case DMLERR_POKEACKTIMEOUT:
+            err = _("a request for a synchronous poke transaction has timed out.");
+            break;
+        case DMLERR_POSTMSG_FAILED:
+            err = _("an internal call to the PostMessage function has failed. ");
+            break;
+        case DMLERR_REENTRANCY:
+            err = _("reentrancy problem.");
+            break;
+        case DMLERR_SERVER_DIED:
+            err = _("a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.");
+            break;
+        case DMLERR_SYS_ERROR:
+            err = _("an internal error has occurred in the DDEML.");
+            break;
+        case DMLERR_UNADVACKTIMEOUT:
+            err = _("a request to end an advise transaction has timed out.");
+            break;
+        case DMLERR_UNFOUND_QUEUE_ID:
+            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.");
+            break;
+        default:
+            err.Printf(_("Unknown DDE error %08x"), error);
     }
-  }
-  return 0;
-}
 
-// Atom table stuff
-static HSZ DDEAddAtom(const wxString& string)
-{
-  HSZ atom = DdeCreateStringHandle(DDEIdInst, (char*) (const char *)string, CP_WINANSI);
-  wxAtomTable.Append(string, (wxObject *)atom);
-  return atom;
-}
-
-static HSZ DDEGetAtom(const wxString& string)
-{
-  wxNode *node = wxAtomTable.Find(string);
-  if (node)
-    return (HSZ)node->Data();
-  else
-  {
-    DDEAddAtom(string);
-    return (HSZ)(wxAtomTable.Find(string)->Data());
-  }
-}
-
-void DDEPrintError(void)
-{
-  char *err = NULL;
-  switch (DdeGetLastError(DDEIdInst))
-  {
-    case DMLERR_ADVACKTIMEOUT:
-      err = "A request for a synchronous advise transaction has timed out.";
-      break;
-    case DMLERR_BUSY:
-      err = "The response to the transaction caused the DDE_FBUSY bit to be set.";
-      break;
-    case DMLERR_DATAACKTIMEOUT:
-      err = "A request for a synchronous data transaction has timed out.";
-      break;
-    case DMLERR_DLL_NOT_INITIALIZED:
-      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.";
-      break;
-    case DMLERR_DLL_USAGE:
-      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.";
-      break;
-    case DMLERR_EXECACKTIMEOUT:
-      err = "A request for a synchronous execute transaction has timed out.";
-      break;
-    case DMLERR_INVALIDPARAMETER:
-      err = "A parameter failed to be validated by the DDEML.";
-      break;
-    case DMLERR_LOW_MEMORY:
-      err = "A DDEML application has created a prolonged race condition.";
-      break;
-    case DMLERR_MEMORY_ERROR:
-      err = "A memory allocation failed.";
-      break;
-    case DMLERR_NO_CONV_ESTABLISHED:
-      err = "A client's attempt to establish a conversation has failed.";
-      break;
-    case DMLERR_NOTPROCESSED:
-      err = "A transaction failed.";
-      break;
-    case DMLERR_POKEACKTIMEOUT:
-      err = "A request for a synchronous poke transaction has timed out.";
-      break;
-    case DMLERR_POSTMSG_FAILED:
-      err = "An internal call to the PostMessage function has failed. ";
-      break;
-    case DMLERR_REENTRANCY:
-      err = "Reentrancy problem.";
-      break;
-    case DMLERR_SERVER_DIED:
-      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.";
-      break;
-    case DMLERR_SYS_ERROR:
-      err = "An internal error has occurred in the DDEML.";
-      break;
-    case DMLERR_UNADVACKTIMEOUT:
-      err = "A request to end an advise transaction has timed out.";
-      break;
-    case DMLERR_UNFOUND_QUEUE_ID:
-      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.";
-      break;
-    default:
-      err = "Unrecognised error type.";
-      break;
-  }
-  MessageBox((HWND) NULL, (LPCSTR)err, "DDE Error", MB_OK | MB_ICONINFORMATION);
+    return err;
 }
 
 #endif