X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/837e57436a89516a5fa9f478f401e06bd872c17c..2353544718a1aff4e12c75c823aa4136d2304027:/src/msw/dde.cpp diff --git a/src/msw/dde.cpp b/src/msw/dde.cpp index 2d28519ecc..d69720c31d 100644 --- a/src/msw/dde.cpp +++ b/src/msw/dde.cpp @@ -1,803 +1,1094 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: dde.cpp +// Name: 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" #endif #include "wx/module.h" #include "wx/dde.h" +#include "wx/intl.h" +#include "wx/hashmap.h" #include "wx/msw/private.h" -#include -#include - -#ifndef __TWIN32__ -#ifdef __GNUWIN32__ -#include "wx/msw/gnuwin32/extra.h" -#endif -#endif #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); -static DWORD DDEIdInst = 0L; -static wxDDEConnection *DDECurrentlyConnecting = NULL; +// string handles +static HSZ DDEAtomFromString(const wxString& s); +static wxString DDEStringFromAtom(HSZ hsz); +static void DDEFreeString(HSZ hsz); -static wxList wxAtomTable(wxKEY_STRING); -static wxList wxDDEClientObjects; -static wxList wxDDEServerObjects; +// error handling +static wxString DDEGetErrorMsg(UINT error); +static void DDELogError(const wxString& s, UINT error = DMLERR_NO_ERROR); -char *DDEDefaultIPCBuffer = NULL; -int DDEDefaultIPCBufferSize = 0; +// ---------------------------------------------------------------------------- +// global variables +// ---------------------------------------------------------------------------- -/* - * Initialization - * - */ +WX_DECLARE_STRING_HASH_MAP( HSZ, wxAtomMap ); -static bool DDEInitialized = FALSE; +static DWORD DDEIdInst = 0L; +static wxDDEConnection *DDECurrentlyConnecting = NULL; +static wxAtomMap wxAtomTable; -void wxDDEInitialize() -{ - if (DDEInitialized) - return; - DDEInitialized = TRUE; +#include "wx/listimpl.cpp" - // Should insert filter flags - DdeInitialize(&DDEIdInst, (PFNCALLBACK)MakeProcInstance( - (FARPROC)_DDECallback, wxGetInstance()), - APPCLASS_STANDARD, - 0L); -} +WX_DEFINE_LIST(wxDDEClientList) +WX_DEFINE_LIST(wxDDEServerList) +WX_DEFINE_LIST(wxDDEConnectionList) -/* - * CleanUp - */ +static wxDDEClientList wxDDEClientObjects; +static wxDDEServerList wxDDEServerObjects; -void wxDDECleanUp() -{ - if (DDEIdInst != 0) - { - DdeUninitialize(DDEIdInst); - DDEIdInst = 0; - } - if (DDEDefaultIPCBuffer) - delete [] DDEDefaultIPCBuffer ; -} +static bool DDEInitialized = false; + +// ---------------------------------------------------------------------------- +// private classes +// ---------------------------------------------------------------------------- -// A module to allow DDE initialization/cleanup -// without calling these functions from app.cpp or from -// the user's application. +// A module to allow DDE cleanup without calling these functions +// from app.cpp or from the user's application. -class wxDDEModule: public wxModule +class wxDDEModule : public wxModule { -DECLARE_DYNAMIC_CLASS(wxDDEModule) public: wxDDEModule() {} - bool OnInit() { wxDDEInitialize(); return TRUE; }; - void OnExit() { wxDDECleanUp(); }; + bool OnInit() { return true; } + void OnExit() { wxDDECleanUp(); } + +private: + DECLARE_DYNAMIC_CLASS(wxDDEModule) }; +// ---------------------------------------------------------------------------- +// 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() +{ + // 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() { - 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, WXSTRINGCAST server_name, CP_WINANSI); + m_serviceName = server; + + HSZ hsz = DDEAtomFromString(server); + + if ( !hsz ) + { + return false; + } + + + bool success = (DdeNameService(DDEIdInst, hsz, (HSZ) NULL, DNS_REGISTER) + != NULL); - if (DdeNameService(DDEIdInst, serviceName, (HSZ) NULL, DNS_REGISTER) == 0) - { - DDEPrintError(); - return FALSE; - } - return TRUE; + 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 != _T("")) - { - HSZ serviceName = DdeCreateStringHandle(DDEIdInst, WXSTRINGCAST m_serviceName, CP_WINANSI); - if (DdeNameService(DDEIdInst, serviceName, NULL, DNS_UNREGISTER) == 0) + if ( !m_serviceName.IsEmpty() ) { - 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); + + 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.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 = 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, WXSTRINGCAST server_name, CP_WINANSI); - HSZ topic_atom = DdeCreateStringHandle(DDEIdInst, WXSTRINGCAST topic, CP_WINANSI); + HSZ hszServer = DDEAtomFromString(server); - HCONV hConv = DdeConnect(DDEIdInst, serviceName, topic_atom, (PCONVCONTEXT)NULL); - if (hConv == (HCONV) NULL) - return (wxConnectionBase*) NULL; - else - { - wxDDEConnection *connection = (wxDDEConnection*) OnMakeConnection(); - if (connection) + if ( !hszServer ) + { + return (wxConnectionBase*) 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 ) { - 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 return (wxConnectionBase*) NULL; - } + 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 = 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(wxChar *buffer, int 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_topicName = ""; + m_client = NULL; + m_server = NULL; - 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(_T("Failed to disconnect from DDE server gracefully")); + } + + SetConnected( false ); // so we don't try and disconnect again + + return ok; } -bool wxDDEConnection::Execute(wxChar *data, int size, wxIPCFormat format) +bool wxDDEConnection::Execute(const wxChar *data, int size, wxIPCFormat WXUNUSED(format)) { - DWORD result; - if (size < 0) - size = wxStrlen(data); + DWORD result; + if (size < 0) + { + size = (wxStrlen(data) + 1) * sizeof(wxChar); // includes final NUL + } - size ++; + bool ok = DdeClientTransaction((LPBYTE)data, + size, + GetHConv(), + NULL, +// 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 (DdeClientTransaction((LPBYTE)data, size, (HCONV) m_hConv, - NULL, format, XTYP_EXECUTE, 5000, &result) ? TRUE : FALSE); + return ok; } -char *wxDDEConnection::Request(const wxString& item, int *size, wxIPCFormat format) +wxChar *wxDDEConnection::Request(const wxString& item, int *size, wxIPCFormat format) { - DWORD result; - HSZ atom = DDEGetAtom(item); + DWORD result; + + HSZ atom = DDEGetAtom(item); - HDDEDATA returned_data = DdeClientTransaction(NULL, 0, (HCONV) m_hConv, - atom, format, XTYP_REQUEST, 5000, &result); + HDDEDATA returned_data = DdeClientTransaction(NULL, 0, + GetHConv(), + atom, format, + XTYP_REQUEST, + DDE_TIMEOUT, + &result); + if ( !returned_data ) + { + DDELogError(_T("DDE data request failed")); + + return NULL; + } - DWORD len = DdeGetData(returned_data, (LPBYTE)(m_bufPtr), m_bufSize, 0); + DWORD len = DdeGetData(returned_data, NULL, 0, 0); - DdeFreeDataHandle(returned_data); + wxChar *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 m_bufPtr; - } - else return NULL; + (void) DdeFreeDataHandle(returned_data); + + if (size) + *size = (int)len; + + return data; } bool wxDDEConnection::Poke(const wxString& item, wxChar *data, int size, wxIPCFormat format) { - DWORD result; - if (size < 0) - size = wxStrlen(data); + DWORD result; + if (size < 0) + { + size = (wxStrlen(data) + 1) * sizeof(wxChar); // includes final NUL + } - size ++; + 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, wxChar *data, int size, wxIPCFormat format) +bool wxDDEConnection::Advise(const wxString& item, + wxChar *data, + int size, + wxIPCFormat format) { - if (size < 0) - size = wxStrlen(data); + if (size < 0) + { + size = (wxStrlen(data) + 1) * sizeof(wxChar); // includes final NUL + } - 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; - 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); + bool ok = DdePostAdvise(DDEIdInst, topic_atom, item_atom) != 0; + if ( !ok ) + { + DDELogError(_("Failed to send DDE advise notification")); + } + + return ok; } -bool wxDDEConnection::OnDisconnect(void) +bool wxDDEConnection::OnDisconnect() { - delete this; - return TRUE; + delete this; + return true; } +// ---------------------------------------------------------------------------- +// _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: - { - wxChar topic_buf[100]; - wxChar server_buf[100]; - DdeQueryString(DDEIdInst, hsz1, (LPTSTR)topic_buf, WXSIZEOF(topic_buf), - CP_WINANSI); - DdeQueryString(DDEIdInst, hsz2, (LPTSTR)server_buf, WXSIZEOF(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); + + wxChar *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 cannot be used for arbitrary data, but only for text + if ( connection->OnExecute(connection->m_topicName, + data, + (int)len, + wxIPC_TEXT ) ) + { + return (DDERETURN)(DWORD)DDE_FACK; + } + } + + return (DDERETURN)DDE_FNOTPROCESSED; + } + + case XTYP_REQUEST: + { + wxDDEConnection *connection = DDEFindConnection(hConv); + + if (connection) + { + wxString item_name = DDEStringFromAtom(hsz2); + + int user_size = -1; + wxChar *data = connection->OnRequest(connection->m_topicName, + item_name, + &user_size, + (wxIPCFormat) wFmt); + if (data) + { + if (user_size < 0) + user_size = (wxStrlen((wxChar*)data) + 1) * sizeof(wxChar); // includes final NUL + + 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); + + wxChar *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); + + wxChar *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->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) - { - wxChar item_name[200]; - DdeQueryString(DDEIdInst, hsz2, (LPTSTR)item_name, WXSIZEOF(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, _T("DDE not initialized") ); - case XTYP_POKE: + HSZ hsz = DdeCreateStringHandle(DDEIdInst, (wxChar*) s.c_str(), DDE_CP); + if ( !hsz ) { - wxDDEConnection *connection = DDEFindConnection(hConv); - - if (connection) - { - wxChar item_name[200]; - DdeQueryString(DDEIdInst, hsz2, (LPTSTR)item_name, WXSIZEOF(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) - { - wxChar item_name[200]; - DdeQueryString(DDEIdInst, hsz2, (LPTSTR)item_name, WXSIZEOF(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) - { - wxChar item_name[200]; - DdeQueryString(DDEIdInst, hsz2, (LPTSTR)item_name, WXSIZEOF(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) - { - wxChar item_name[200]; - DdeQueryString(DDEIdInst, hsz2, (LPTSTR)item_name, WXSIZEOF(item_name), - CP_WINANSI); + wxLogError(s + _T(": ") + 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, WXSTRINGCAST 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) -{ - wxChar *err = NULL; - switch (DdeGetLastError(DDEIdInst)) - { - case DMLERR_ADVACKTIMEOUT: - err = _T("A request for a synchronous advise transaction has timed out."); - break; - case DMLERR_BUSY: - err = _T("The response to the transaction caused the DDE_FBUSY bit to be set."); - break; - case DMLERR_DATAACKTIMEOUT: - err = _T("A request for a synchronous data transaction has timed out."); - break; - case DMLERR_DLL_NOT_INITIALIZED: - err = _T("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 = _T("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 = _T("A request for a synchronous execute transaction has timed out."); - break; - case DMLERR_INVALIDPARAMETER: - err = _T("A parameter failed to be validated by the DDEML."); - break; - case DMLERR_LOW_MEMORY: - err = _T("A DDEML application has created a prolonged race condition."); - break; - case DMLERR_MEMORY_ERROR: - err = _T("A memory allocation failed."); - break; - case DMLERR_NO_CONV_ESTABLISHED: - err = _T("A client's attempt to establish a conversation has failed."); - break; - case DMLERR_NOTPROCESSED: - err = _T("A transaction failed."); - break; - case DMLERR_POKEACKTIMEOUT: - err = _T("A request for a synchronous poke transaction has timed out."); - break; - case DMLERR_POSTMSG_FAILED: - err = _T("An internal call to the PostMessage function has failed. "); - break; - case DMLERR_REENTRANCY: - err = _T("Reentrancy problem."); - break; - case DMLERR_SERVER_DIED: - err = _T("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 = _T("An internal error has occurred in the DDEML."); - break; - case DMLERR_UNADVACKTIMEOUT: - err = _T("A request to end an advise transaction has timed out."); - break; - case DMLERR_UNFOUND_QUEUE_ID: - err = _T("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 = _T("Unrecognised error type."); - break; - } - MessageBox((HWND) NULL, (LPCTSTR)err, _T("DDE Error"), MB_OK | MB_ICONINFORMATION); + return err; } #endif