X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/2bda0e173844e8e0f8acf4e8ad8b5c26e5c6fe5d..9d5507f7a2701395e1d5c121bd877bb9066ee6ea:/src/msw/dde.cpp diff --git a/src/msw/dde.cpp b/src/msw/dde.cpp index e8c8bd1121..7604068ac6 100644 --- a/src/msw/dde.cpp +++ b/src/msw/dde.cpp @@ -1,785 +1,1182 @@ ///////////////////////////////////////////////////////////////////////////// -// 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 + #pragma hdrstop #endif -#ifndef WX_PRECOMP -#include "wx/defs.h" -#endif - -#if USE_IPC +#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" -#ifdef __GNUWIN32__ -#include "wx/msw/gnuwin32/extra.h" -#endif +#include "wx/msw/private.h" -#include -#include #include +#include + +// ---------------------------------------------------------------------------- +// 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" -static wxList wxAtomTable(wxKEY_STRING); -static wxList wxDDEClientObjects; -static wxList wxDDEServerObjects; +WX_DEFINE_LIST(wxDDEClientList) +WX_DEFINE_LIST(wxDDEServerList) +WX_DEFINE_LIST(wxDDEConnectionList) -char *DDEDefaultIPCBuffer = NULL; -int DDEDefaultIPCBufferSize = 0; +static wxDDEClientList wxDDEClientObjects; +static wxDDEServerList wxDDEServerObjects; -/* - * Initialization - * - */ +static bool DDEInitialized = false; -static bool DDEInitialized = FALSE; +// ---------------------------------------------------------------------------- +// private classes +// ---------------------------------------------------------------------------- -void wxDDEInitialize() +// A module to allow DDE cleanup without calling these functions +// from app.cpp or from the user's application. + +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(_T("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(), + _T("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() { - service_name = ""; - wxDDEServerObjects.Append(this); + wxDDEInitialize(); + + wxDDEServerObjects.Append(this); } -bool wxDDEServer::Create(const wxString& server_name) +bool wxDDEServer::Create(const wxString& server) { - service_name = 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, 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 (service_name != "") - { - HSZ serviceName = DdeCreateStringHandle(DDEIdInst, (char*) (const char *)service_name, 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 = 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); + + 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 = connections.First(); - while (node) - { - wxDDEConnection *connection = (wxDDEConnection *)node->Data(); - wxNode *next = node->Next(); - delete connection; - 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 = connections.First(); - wxDDEConnection *found = NULL; - while (node && !found) - { - wxDDEConnection *connection = (wxDDEConnection *)node->Data(); - if (connection->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 = connections.First(); - bool found = FALSE; - while (node && !found) - { - wxDDEConnection *connection = (wxDDEConnection *)node->Data(); - if (connection->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 = connections.First(); - while (node) - { - wxDDEConnection *connection = (wxDDEConnection *)node->Data(); - delete connection; // Deletes the node implicitly (see ~wxDDEConnection) - node = 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); - HCONV hConv = DdeConnect(DDEIdInst, serviceName, topic_atom, (PCONVCONTEXT)NULL); - if (hConv == NULL) - return NULL; - else - { - wxDDEConnection *connection = (wxDDEConnection*) OnMakeConnection(); - if (connection) + if ( !hszServer ) { - connection->hConv = (WXHCONV) hConv; - connection->topic_name = topic; - connection->client = this; - connections.Append(connection); - return connection; + return (wxConnectionBase*) NULL; } - else return NULL; - } + + + HSZ hszTopic = DDEAtomFromString(topic); + + if ( !hszTopic ) + { + DDEFreeString(hszServer); + return (wxConnectionBase*) NULL; + } + + + HCONV hConv = ::DdeConnect(DDEIdInst, hszServer, hszTopic, + (PCONVCONTEXT) NULL); + + DDEFreeString(hszServer); + DDEFreeString(hszTopic); + + + if ( !hConv ) + { + 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; + } + } + + return (wxConnectionBase*) NULL; } -wxConnectionBase *wxDDEClient::OnMakeConnection(void) +wxConnectionBase *wxDDEClient::OnMakeConnection() { - return new wxDDEConnection; + return new wxDDEConnection; } wxDDEConnection *wxDDEClient::FindConnection(WXHCONV conv) { - wxNode *node = connections.First(); - wxDDEConnection *found = NULL; - while (node && !found) - { - wxDDEConnection *connection = (wxDDEConnection *)node->Data(); - if (connection->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 = connections.First(); - bool found = FALSE; - while (node && !found) - { - wxDDEConnection *connection = (wxDDEConnection *)node->Data(); - if (connection->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]; - buf_ptr = DDEDefaultIPCBuffer; - buf_size = DDEDefaultIPCBufferSize; - } - else - { - buf_ptr = buffer; - buf_size = size; - } - - topic_name = ""; + m_client = NULL; + m_server = NULL; - client = NULL; - server = NULL; - - hConv = 0; - sending_data = NULL; + m_hConv = 0; + m_sendingData = NULL; } -wxDDEConnection::wxDDEConnection(void) +wxDDEConnection::wxDDEConnection() + : wxConnectionBase() { - hConv = 0; - sending_data = NULL; - server = NULL; - client = NULL; - if (DDEDefaultIPCBuffer == NULL) - DDEDefaultIPCBuffer = new char[DDEDefaultIPCBufferSize]; - - buf_ptr = DDEDefaultIPCBuffer; - buf_size = DDEDefaultIPCBufferSize; - topic_name = ""; + m_hConv = 0; + m_sendingData = NULL; + m_server = NULL; + m_client = NULL; } -wxDDEConnection::~wxDDEConnection(void) +wxDDEConnection::~wxDDEConnection() { - if (server) - server->GetConnections().DeleteObject(this); - else - 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) hConv); - return (DdeDisconnect((HCONV) hConv) != 0); + if ( !GetConnected() ) + return true; + + DDEDeleteConnection(GetHConv()); + + bool ok = DdeDisconnect(GetHConv()) != 0; + if ( !ok ) + { + DDELogError(_T("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, int 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, + _T("wxDDEServer::Execute() supports only text data") ); + + wxMemoryBuffer buffer; + LPBYTE realData wxDUMMY_INITIALIZE(NULL); + size_t realSize wxDUMMY_INITIALIZE(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; + } - 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( _T("UTF-8 text not supported in ANSI build") ); + + return false; + } + else // don't convert wxIPC_TEXT + { + realData = (LPBYTE)data; + realSize = size; + } + + if ( conv ) + { + const wchar_t * const wtext = (const wchar_t *)data; + const size_t len = size/sizeof(wchar_t); - return (DdeClientTransaction((LPBYTE)data, size, (HCONV) hConv, - NULL, format, XTYP_EXECUTE, 5000, &result) ? TRUE : FALSE); + 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, + 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(_T("DDE execute request failed")); + } + + return ok; } -char *wxDDEConnection::Request(const wxString& item, int *size, int format) +const void *wxDDEConnection::Request(const wxString& item, size_t *size, wxIPCFormat format) { - DWORD result; - HSZ atom = DDEGetAtom(item); + DWORD result; - HDDEDATA returned_data = DdeClientTransaction(NULL, 0, (HCONV) hConv, - atom, format, XTYP_REQUEST, 5000, &result); + HSZ atom = DDEGetAtom(item); - DWORD len = DdeGetData(returned_data, (LPBYTE)(buf_ptr), buf_size, 0); + HDDEDATA returned_data = DdeClientTransaction(NULL, 0, + GetHConv(), + atom, format, + XTYP_REQUEST, + DDE_TIMEOUT, + &result); + if ( !returned_data ) + { + DDELogError(_T("DDE data request failed")); - DdeFreeDataHandle(returned_data); + return NULL; + } + + DWORD len = DdeGetData(returned_data, NULL, 0, 0); + + void *data = GetBufferAtLeast(len); + wxASSERT_MSG(data != NULL, + _T("Buffer too small in wxDDEConnection::Request") ); + (void) DdeGetData(returned_data, (LPBYTE)data, len, 0); - if (size) *size = (int)len; - if (len > 0) - { - return buf_ptr; - } - else return NULL; + (void) DdeFreeDataHandle(returned_data); + + if (size) + *size = (size_t)len; + + return data; } -bool wxDDEConnection::Poke(const wxString& item, char *data, int size, int 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) 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) 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) 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, int 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; // 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")); + } - HSZ item_atom = DDEGetAtom(item); - HSZ topic_atom = DDEGetAtom(topic_name); - sending_data = data; - data_size = size; - data_type = format; - return (DdePostAdvise(DDEIdInst, topic_atom, item_atom) != 0); -} - -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->server = server; - server->GetConnections().Append(connection); - connection->hConv = 0; - connection->topic_name = 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->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, + _T("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, + _T("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, + _T("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->buf_ptr), connection->buf_size, 0); - DdeFreeDataHandle(hData); - if (connection->OnExecute(connection->topic_name, connection->buf_ptr, (int)len, 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->topic_name, wxString(item_name), &user_size, 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, _T("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->buf_ptr), connection->buf_size, 0); - DdeFreeDataHandle(hData); - connection->OnPoke(connection->topic_name, wxString(item_name), connection->buf_ptr, (int)len, 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->topic_name, 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->topic_name, 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->sending_data) - { - HDDEDATA data = DdeCreateDataHandle(DDEIdInst, - (LPBYTE)connection->sending_data, - connection->data_size, 0, hsz2, connection->data_type, 0); - connection->sending_data = 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 + _T(": ") + DDEGetErrorMsg(error)); +} - DWORD len = DdeGetData(hData, (LPBYTE)(connection->buf_ptr), connection->buf_size, 0); - DdeFreeDataHandle(hData); - if (connection->OnAdvise(connection->topic_name, wxString(item_name), connection->buf_ptr, (int)len, 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(NULL, (LPCSTR)err, "DDE Error", MB_OK | MB_ICONINFORMATION); + return err; } #endif - // USE_IPC + // wxUSE_IPC