Applied patch [ 600051 ] DDE and TCP improvements and fixes

By Michael Fielding

As discussed on wx-dev. some fixes and improvements for Interprocess Communication (IPC), using DDE and TCP.

1. DDE buffers were using a global buffer
2. TCP buffers were allocated each time needed, and Request would have caused memory leaks had it been used.

Fixed these both by using a self-resizing buffer in wxConnectionBase. Changed samples and docs to reflect the improved (but backward compatible) internal buffer management. wxConnectionBase could (in future) use wxMemoryBuffer.

3. IPC sample had trouble closing, causing crash, when closing server using window X button.

Because it was (effectively) trying to delete a window in OnExit, when that window was already destroyed. Fixed by making IPCDialog and MyConnection remember if they'd destroyed each other. It's not elegant, but either the connection or the window could be deleted first.

4. Docs for wxDDE... and wxTCP... duplicated eachother, supposed to have same API. Some parts unclear.

Patch removes dde and tcp-specific files (including from tipc.tex and classes.tex), and explains how ipc.h selects for you which one to use based on platform. Some other misc clarifications.

6. Client sample was suffering apparent memory leak because of not deleting connection object, and had a hack in there to do that.

In fact this was due to the derived OnDisconnect not deleting itself, as it does in base class. Mentioned need to do it in docs, fixed sample so that it does.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@16907 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Julian Smart
2002-09-01 14:48:16 +00:00
parent 1affde0a6b
commit f010ad4812
16 changed files with 226 additions and 182 deletions

View File

@@ -49,6 +49,8 @@ wxBase:
must modify YourApp::OnAssert() signature if you were using it to override must modify YourApp::OnAssert() signature if you were using it to override
the default assert handling. the default assert handling.
- IPC classes improved and memory leaks fixed (Michael Fielding).
Global buffer removed, duplication in docs removed.
All (GUI): All (GUI):

View File

@@ -633,3 +633,4 @@ Calling Yield() recursively is normally an error and an assert failure is
raised in debug build if such situation is detected. However if the the raised in debug build if such situation is detected. However if the the
{\it onlyIfNeeded} parameter is {\tt TRUE}, the method will just silently {\it onlyIfNeeded} parameter is {\tt TRUE}, the method will just silently
return {\tt FALSE} instead. return {\tt FALSE} instead.

View File

@@ -366,17 +366,14 @@ wxWindows provides its own classes for socket based networking.
\overview{Overview}{ipcoverview} \overview{Overview}{ipcoverview}
wxWindows provides a simple interprocess communications facilities wxWindows provides simple interprocess communications facilities
based on DDE. based on Windows DDE, but available on most platforms using TCP.
\twocolwidtha{6cm} \twocolwidtha{6cm}
\begin{twocollist}\itemsep=0pt \begin{twocollist}\itemsep=0pt
\twocolitem{\helpref{wxDDEClient}{wxddeclient}}{Represents a client} \twocolitem{\helpref{wxClient}{wxddeclient}}{Represents a client}
\twocolitem{\helpref{wxDDEConnection}{wxddeconnection}}{Represents the connection between a client and a server} \twocolitem{\helpref{wxConnection}{wxddeconnection}}{Represents the connection between a client and a server}
\twocolitem{\helpref{wxDDEServer}{wxddeserver}}{Represents a server} \twocolitem{\helpref{wxServer}{wxddeserver}}{Represents a server}
\twocolitem{\helpref{wxTCPClient}{wxtcpclient}}{Represents a client}
\twocolitem{\helpref{wxTCPConnection}{wxtcpconnection}}{Represents the connection between a client and a server}
\twocolitem{\helpref{wxTCPServer}{wxtcpserver}}{Represents a server}
%\twocolitem{\helpref{wxSocketHandler}{wxsockethandler}}{Represents a socket handler} %\twocolitem{\helpref{wxSocketHandler}{wxsockethandler}}{Represents a socket handler}
\end{twocollist} \end{twocollist}
@@ -623,4 +620,3 @@ using any of them in new programs:
\twocolitem{\helpref{wxQuantize}{wxquantize}}{Class to perform quantization, or colour reduction} \twocolitem{\helpref{wxQuantize}{wxquantize}}{Class to perform quantization, or colour reduction}
\twocolitem{\helpref{wxSingleInstanceChecker}{wxsingleinstancechecker}}{Check that only single program instance is running} \twocolitem{\helpref{wxSingleInstanceChecker}{wxsingleinstancechecker}}{Check that only single program instance is running}
\end{twocollist} \end{twocollist}

View File

@@ -29,6 +29,7 @@
\input checklst.tex \input checklst.tex
\input choice.tex \input choice.tex
\input clasinfo.tex \input clasinfo.tex
\input ipcclint.tex
\input clientdc.tex \input clientdc.tex
\input clientdat.tex \input clientdat.tex
\input clipbrd.tex \input clipbrd.tex
@@ -42,6 +43,7 @@
\input cmdproc.tex \input cmdproc.tex
\input conditn.tex \input conditn.tex
\input config.tex \input config.tex
\input ipcconn.tex
\input cshelp.tex \input cshelp.tex
\input control.tex \input control.tex
\input countstr.tex \input countstr.tex
@@ -62,9 +64,6 @@
\input datetime.tex \input datetime.tex
\input db.tex \input db.tex
\input dc.tex \input dc.tex
\input ddeclint.tex
\input ddeconn.tex
\input ddeservr.tex
\input debugcxt.tex \input debugcxt.tex
\input dialog.tex \input dialog.tex
\input dialevt.tex \input dialevt.tex
@@ -248,6 +247,7 @@
\input scrolevt.tex \input scrolevt.tex
\input scrlwevt.tex \input scrlwevt.tex
\input semaphor.tex \input semaphor.tex
\input ipcservr.tex
\input hprovsmp.tex \input hprovsmp.tex
\input sngchdlg.tex \input sngchdlg.tex
\input snglinst.tex \input snglinst.tex
@@ -285,9 +285,6 @@
\input tabctrl.tex \input tabctrl.tex
\input tabevent.tex \input tabevent.tex
\input taskbar.tex \input taskbar.tex
\input tcpclint.tex
\input tcpconn.tex
\input tcpservr.tex
\input tempfile.tex \input tempfile.tex
\input text.tex \input text.tex
\input txtdatob.tex \input txtdatob.tex
@@ -325,4 +322,3 @@
\input xmlresh.tex \input xmlresh.tex
\input zipstrm.tex \input zipstrm.tex
\input strmzlib.tex \input strmzlib.tex

View File

@@ -69,4 +69,3 @@ store application-specific data in instances of the new class.
Returns TRUE if this is a valid host name, FALSE otherwise. This always Returns TRUE if this is a valid host name, FALSE otherwise. This always
returns TRUE under MS Windows. returns TRUE under MS Windows.

View File

@@ -199,4 +199,3 @@ Called by the client application to ask if an advise loop can be
stopped. Causes the server connection's \helpref{wxDDEConnection::OnStopAdvise}{wxddeconnectiononstopadvise} member stopped. Causes the server connection's \helpref{wxDDEConnection::OnStopAdvise}{wxddeconnectiononstopadvise} member
to be called. Returns TRUE if the server okays it, FALSE otherwise. to be called. Returns TRUE if the server okays it, FALSE otherwise.

View File

@@ -49,4 +49,3 @@ Under UNIX, when a server is created the OnAcceptConnection message is
always sent for standard input and output, but in the context of DDE always sent for standard input and output, but in the context of DDE
messages it doesn't make a lot of sense. messages it doesn't make a lot of sense.

View File

@@ -1,112 +1,127 @@
\section{Interprocess communication overview}\label{ipcoverview} \section{Interprocess communication overview}\label{ipcoverview}
Classes: \helpref{wxDDEServer}{wxddeserver}, \helpref{wxDDEConnection}{wxddeconnection}, Classes: \helpref{wxServer}{wxddeserver},
\helpref{wxDDEClient}{wxddeclient}, \helpref{wxConnection}{wxddeconnection},
\helpref{wxTCPServer}{wxtcpserver}, \helpref{wxTCPConnection}{wxtcpconnection}, \helpref{wxClient}{wxddeclient}
\helpref{wxTCPClient}{wxtcpclient} %\helpref{wxTCPServer}{wxtcpserver}, \helpref{wxTCPConnection}{wxtcpconnection},
%\helpref{wxTCPClient}{wxtcpclient}
wxWindows has a number of different classes to help with interprocess communication wxWindows has a number of different classes to help with
and network programming. This section only discusses one family of classes - the DDE-like interprocess communication and network programming. This section
protocol - but here's a list of other useful classes: only discusses one family of classes -- the DDE-like protocol --
but here's a list of other useful classes:
\begin{itemize}\itemsep=0pt \begin{itemize}\itemsep=0pt
\item \helpref{wxSocketEvent}{wxsocketevent}, \item \helpref{wxSocketEvent}{wxsocketevent},
\helpref{wxSocketBase}{wxsocketbase}, \helpref{wxSocketBase}{wxsocketbase},
\helpref{wxSocketClient}{wxsocketclient}, \helpref{wxSocketClient}{wxsocketclient},
\helpref{wxSocketServer}{wxsocketserver}: classes for the low-level TCP/IP API. \helpref{wxSocketServer}{wxsocketserver}: classes for the low-level TCP/IP API.
\item \helpref{wxProtocol}{wxprotocol}, \helpref{wxURL}{wxurl}, \helpref{wxFTP}{wxftp}, wxHTTP: classes \item \helpref{wxProtocol}{wxprotocol}, \helpref{wxURL}{wxurl}, \helpref{wxFTP}{wxftp}, \helpref{wxHTTP}{wxhttp}: classes
for programming popular Internet protocols. for programming popular Internet protocols.
\end{itemize} \end{itemize}
Further information on these classes will be available in due course. wxWindows' DDE-like protocol is a high-level protocol based on
Windows DDE. There are two implementations of this DDE-like
protocol: one using real DDE running on Windows only, and another
using TCP/IP (sockets) that runs on most platforms. Since the API
and virtually all of the behaviour is the same apart from the
names of the classes, you should find it easy to switch between
the two implementations.
Notice that by including {\tt <wx/ipc.h>} you may define convnient synonyms for Notice that by including {\tt <wx/ipc.h>} you may define
the IPC classes: {\tt wxServer} for either {\tt wxDDEServer} or convenient synonyms for the IPC classes: {\tt wxServer} for either
{\tt wxTCPServer} depending on whether DDE-based or socket-based implementation {\tt wxDDEServer} or {\tt wxTCPServer} depending on whether
is used and the same thing for {\tt wxClient} and {\tt wxConnection}. By DDE-based or socket-based implementation is used and the same
default, DDE implementation is used under Windows. If you want to use IPC thing for {\tt wxClient} and {\tt wxConnection}.
between the different workstations you should define {\tt wxUSE\_DDE\_FOR\_IPC}
as $0$ before including this header -- this will force using TCP/IP
implementation even under Windows.
By default, DDE implementation is used under Windows. DDE works
only within one computer. If you want to use IPC between
different workstations you should define {\tt
wxUSE\_DDE\_FOR\_IPC} as $0$ before including this header -- this
will force using TCP/IP implementation even under Windows.
wxWindows has a high-level protocol based on Windows DDE. The following description refers to wx... but remember that the
There are two implementations of this DDE-like protocol: equivalent wxTCP... and wxDDE... classes can be used in much the
one using real DDE running on Windows only, and another using TCP/IP (sockets) that runs same way.
on most platforms. Since the API is the same apart from the names of the classes, you
should find it easy to switch between the two implementations.
The following description refers to 'DDE' but remember that the equivalent wxTCP... classes Three classes are central to the DDE-like API:
can be used in much the same way.
Three classes are central to the DDE API:
\begin{enumerate}\itemsep=0pt \begin{enumerate}\itemsep=0pt
\item wxDDEClient. This represents the client application, and is used \item wxClient. This represents the client application, and is used
only within a client program. only within a client program.
\item wxDDEServer. This represents the server application, and is used \item wxServer. This represents the server application, and is used
only within a server program. only within a server program.
\item wxDDEConnection. This represents the connection from the current \item wxConnection. This represents the connection from the
client or server to the other application (server or client), and can be used client to the server - both the client and the server use an
in both server and client programs. Most DDE instance of this class, one per connection. Most DDE transactions
transactions operate on this object. operate on this object.
\end{enumerate} \end{enumerate}
Messages between applications are usually identified by three variables: Messages between applications are usually identified by three
connection object, topic name and item name. A data string is a fourth variables: connection object, topic name and item name. A data
element of some messages. To create a connection (a conversation in string is a fourth element of some messages. To create a
Windows parlance), the client application sends the message connection (a conversation in Windows parlance), the client
MakeConnection to the client object, with a string service name to application uses wxClient::MakeConnection to send a message to the
identify the server and a topic name to identify the topic for the server object, with a string service name to identify the server
duration of the connection. Under Unix, the service name may be either an and a topic name to identify the topic for the duration of the
integer port identifier in which case an Internet domain socket will be used connection. Under Unix, the service name may be either an integer
for the communications or a valid file name (which shouldn't exist and will be port identifier in which case an Internet domain socket will be
deleted afterwards) in which case a Unix domain socket is created. used for the communications or a valid file name (which shouldn't
exist and will be deleted afterwards) in which case a Unix domain
socket is created.
{\bf SECURITY NOTE:} Using Internet domain sockets if extremely insecure for {\bf SECURITY NOTE:} Using Internet domain sockets if extremely insecure for
IPC as there is absolutely no access control for them, use Unix domain sockets IPC as there is absolutely no access control for them, use Unix domain sockets
whenever possible! whenever possible!
The server then responds and either vetoes the connection or allows it. The server then responds and either vetoes the connection or
If allowed, a connection object is created which persists until the allows it. If allowed, both the server and client objects create
connection is closed. The connection object is then used for subsequent wxConnection objects which persist until the connection is
messages between client and server. closed. The connection object is then used for sending and
receiving subsequent messages between client and server -
overriding virtual functions in your class derived from
wxConnection allows you to handle the DDE messages.
To create a working server, the programmer must: To create a working server, the programmer must:
\begin{enumerate}\itemsep=0pt \begin{enumerate}\itemsep=0pt
\item Derive a class from wxDDEServer. \item Derive a class from wxConnection, providing handlers for various messages sent to the server
\item Override the handler OnAcceptConnection for accepting or rejecting a connection, side of a wxConnection (e.g. OnExecute, OnRequest, OnPoke). Only
on the basis of the topic argument. This member must create and return a connection the handlers actually required by the application need to be
object if the connection is accepted. overridden.
\item Create an instance of your server object, and call Create to \item Derive a class from wxServer, overriding OnAcceptConnection
to accept or reject a connection on the basis of the topic
argument. This member must create and return an instance of the
derived connection class if the connection is accepted.
\item Create an instance of your server object and call Create to
activate it, giving it a service name. activate it, giving it a service name.
\item Derive a class from wxDDEConnection.
\item Provide handlers for various messages that are sent to the server
side of a wxDDEConnection.
\end{enumerate} \end{enumerate}
To create a working client, the programmer must: To create a working client, the programmer must:
\begin{enumerate}\itemsep=0pt \begin{enumerate}\itemsep=0pt
\item Derive a class from wxDDEClient. \item Derive a class from wxConnection, providing handlers for various
\item Override the handler OnMakeConnection to create and return messages sent to the client side of a wxConnection (e.g.
an appropriate connection object. OnAdvise). Only the handlers actually required by the application
need to be overridden.
\item Derive a class from wxClient, overriding OnMakeConnection to
create and return an instance of the derived connection class.
\item Create an instance of your client object. \item Create an instance of your client object.
\item Derive a class from wxDDEConnection. \item When appropriate, create a new connection using
\item Provide handlers for various messages that are sent to the client \helpref{wxClient::MakeConnection}{wxddeclientmakeconnection},
side of a wxDDEConnection. with arguments host name (processed in Unix only, use `localhost'
\item When appropriate, create a new connection by sending a MakeConnection for local computer), service name, and topic name for this
message to the client object, with arguments host name (processed in Unix only), connection. The client object will call
service name, and topic name for this connection. The client object will call OnMakeConnection \helpref{OnMakeConnection}{wxddeclientonmakeconnection} to create
to create a connection object of the desired type. a connection object of the derived class if the connection is
\item Use the wxDDEConnection member functions to send messages to the server. successful.
\item Use the wxConnection member functions to send messages to the server.
\end{enumerate} \end{enumerate}
\subsection{Data transfer} \subsection{Data transfer}
These are the ways that data can be transferred from one application to These are the ways that data can be transferred from one
another. application to another. These are methods of wxConnection.
\begin{itemize}\itemsep=0pt \begin{itemize}\itemsep=0pt
\item {\bf Execute:} the client calls the server with a data string representing \item {\bf Execute:} the client calls the server with a data string representing
@@ -140,31 +155,31 @@ that item to be highlighted in the client list box.
\subsection{More DDE details} \subsection{More DDE details}
A wxDDEClient object represents the client part of a client-server DDE A wxClient object initiates the client part of a client-server
(Dynamic Data Exchange) conversation (available in both DDE-like (Dynamic Data Exchange) conversation (available in both
Windows and Unix). Windows and Unix).
To create a client which can communicate with a suitable server, To create a client which can communicate with a suitable server,
you need to derive a class from wxDDEConnection and another from wxDDEClient. you need to derive a class from wxConnection and another from
The custom wxDDEConnection class will intercept communications in wxClient. The custom wxConnection class will receive
a `conversation' with a server, and the custom wxDDEServer is required communications in a `conversation' with a server. and the custom
so that a user-overridden \helpref{wxDDEClient::OnMakeConnection}{wxddeclientonmakeconnection} member can return wxServer is required so that a user-overridden
a wxDDEConnection of the required class, when a connection is made. \helpref{wxDDEClient::OnMakeConnection}{wxddeclientonmakeconnection}
member can return a wxDDEConnection of the required class, when a
connection is made.
For example: For example:
\begin{verbatim} \begin{verbatim}
class MyConnection: public wxDDEConnection class MyConnection: public wxConnection {
{
public: public:
MyConnection(void)::wxDDEConnection(ipc_buffer, 3999) {} MyConnection(void)::wxConnection() {}
~MyConnection(void) { } ~MyConnection(void) { }
bool OnAdvise(const wxString& topic, const wxString& item, char *data, int size, wxIPCFormat format) bool OnAdvise(const wxString& topic, const wxString& item, char *data, int size, wxIPCFormat format)
{ wxMessageBox(topic, data); } { wxMessageBox(topic, data); }
}; };
class MyClient: public wxDDEClient class MyClient: public wxClient {
{
public: public:
MyClient(void) {} MyClient(void) {}
wxConnectionBase *OnMakeConnection(void) { return new MyConnection; } wxConnectionBase *OnMakeConnection(void) { return new MyConnection; }
@@ -172,15 +187,20 @@ class MyClient: public wxDDEClient
\end{verbatim} \end{verbatim}
Here, {\bf MyConnection} will respond to \helpref{OnAdvise}{wxddeconnectiononadvise} messages sent Here, {\bf MyConnection} will respond to
by the server. \helpref{OnAdvise}{wxddeconnectiononadvise} messages sent by the
server by displaying a message box.
When the client application starts, it must create an instance of the derived wxDDEClient. In the following, command line When the client application starts, it must create an instance of
arguments are used to pass the host name (the name of the machine the server is running the derived wxClient. In the following, command line arguments
on) and the server name (identifying the server process). Calling \helpref{wxDDEClient::MakeConnection}{wxddeclientmakeconnection}\rtfsp are used to pass the host name (the name of the machine the
implicitly creates an instance of {\bf MyConnection} if the request for a server is running on) and the server name (identifying the server
connection is accepted, and the client then requests an {\it Advise} loop process). Calling
from the server, where the server calls the client when data has changed. \helpref{wxDDEClient::MakeConnection}{wxddeclientmakeconnection}\rtfsp
implicitly creates an instance of {\bf MyConnection} if the
request for a connection is accepted, and the client then
requests an {\it Advise} loop from the server (an Advise loop is
where the server calls the client when data has changed).
\begin{verbatim} \begin{verbatim}
wxString server = "4242"; wxString server = "4242";

View File

@@ -48,10 +48,12 @@ skipped.
You can easily add runtime-translation capacity by placing each line of the You can easily add runtime-translation capacity by placing each line of the
tips.txt file inside the usual translation macro. For example, your tips.txt tips.txt file inside the usual translation macro. For example, your tips.txt
file would look like this: file would look like this:
\begin{verbatim} \begin{verbatim}
_("This is my first tip") _("This is my first tip")
_("This is my second tip") _("This is my second tip")
\end{verbatim} \end{verbatim}
Now add your tips.txt file into the list of files that gettext searches Now add your tips.txt file into the list of files that gettext searches
for translatable strings. The tips will thus get included into your for translatable strings. The tips will thus get included into your
generated .po file catalog and be translated at runtime along with the rest of generated .po file catalog and be translated at runtime along with the rest of
@@ -64,3 +66,4 @@ a backslash-doublequote.
See the dialogs program in your samples folder for a working example inside a See the dialogs program in your samples folder for a working example inside a
program. program.

View File

@@ -50,8 +50,13 @@ class WXDLLEXPORT wxConnectionBase: public wxObject
DECLARE_CLASS(wxConnectionBase) DECLARE_CLASS(wxConnectionBase)
public: public:
inline wxConnectionBase(void) {} wxConnectionBase(wxChar *buffer, int size); // use external buffer
inline ~wxConnectionBase(void) {} wxConnectionBase(); // use internal, adaptive buffer
wxConnectionBase(wxConnectionBase& copy);
~wxConnectionBase(void);
void SetConnected( bool c ) { m_connected = c; }
bool GetConnected() { return m_connected; }
// Calls that CLIENT can make // Calls that CLIENT can make
virtual bool Execute(const wxChar *data, int size = -1, wxIPCFormat format = wxIPC_TEXT ) = 0; virtual bool Execute(const wxChar *data, int size = -1, wxIPCFormat format = wxIPC_TEXT ) = 0;
@@ -106,8 +111,20 @@ public:
// Callbacks to BOTH - override at will // Callbacks to BOTH - override at will
// Default behaviour is to delete connection and return TRUE // Default behaviour is to delete connection and return TRUE
virtual bool OnDisconnect(void) = 0; virtual bool OnDisconnect(void) = 0;
// return a buffer at least this size, reallocating buffer if needed
// returns NULL if using an inadequate user buffer - it can't be resized
wxChar * GetBufferAtLeast( size_t bytes );
protected:
bool m_connected;
private:
wxChar * m_buffer;
size_t m_buffersize;
bool m_deletebufferwhendone;
}; };
class WXDLLEXPORT wxServerBase: public wxObject class WXDLLEXPORT wxServerBase: public wxObject
{ {
DECLARE_CLASS(wxServerBase) DECLARE_CLASS(wxServerBase)

View File

@@ -61,7 +61,7 @@ class WXDLLEXPORT wxTCPConnection: public wxConnectionBase
DECLARE_DYNAMIC_CLASS(wxTCPConnection) DECLARE_DYNAMIC_CLASS(wxTCPConnection)
public: public:
wxTCPConnection(char *buffer, int size); wxTCPConnection(wxChar *buffer, int size);
wxTCPConnection(); wxTCPConnection();
virtual ~wxTCPConnection(); virtual ~wxTCPConnection();

View File

@@ -30,7 +30,6 @@
// Settings common to both executables: determines whether // Settings common to both executables: determines whether
// we're using TCP/IP or real DDE. // we're using TCP/IP or real DDE.
#include "ddesetup.h" #include "ddesetup.h"
#if defined(__WXGTK__) || defined(__WXX11__) || defined(__WXMOTIF__) || defined(__WXMAC__) #if defined(__WXGTK__) || defined(__WXX11__) || defined(__WXMOTIF__) || defined(__WXMAC__)
@@ -56,7 +55,6 @@ END_EVENT_TABLE()
// globals // globals
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
char ipc_buffer[4000];
wxListBox *the_list = NULL; wxListBox *the_list = NULL;
MyConnection *the_connection = NULL; MyConnection *the_connection = NULL;
@@ -119,16 +117,11 @@ bool MyApp::OnInit()
int MyApp::OnExit() int MyApp::OnExit()
{ {
if (the_connection)
{
the_connection->Disconnect();
delete the_connection;
the_connection = NULL;
}
// will delete the connection too // will delete the connection too
// Update: Seems it didn't delete the_connection, because there's a leak. // Update: Seems it didn't delete the_connection, because there's a leak.
// Deletion is now explicitly done a few lines up. // Deletion is now explicitly done a few lines up.
// another Update: in fact it's because OnDisconnect should delete it, but
// it wasn't
delete my_client; delete my_client;
@@ -203,11 +196,6 @@ wxConnectionBase *MyClient::OnMakeConnection()
return new MyConnection; return new MyConnection;
} }
MyConnection::MyConnection()
: wxConnection(ipc_buffer, WXSIZEOF(ipc_buffer))
{
}
bool MyConnection::OnAdvise(const wxString& topic, const wxString& item, char *data, int size, wxIPCFormat format) bool MyConnection::OnAdvise(const wxString& topic, const wxString& item, char *data, int size, wxIPCFormat format)
{ {
if (the_list) if (the_list)
@@ -221,10 +209,13 @@ bool MyConnection::OnAdvise(const wxString& topic, const wxString& item, char *d
bool MyConnection::OnDisconnect() bool MyConnection::OnDisconnect()
{ {
// when connection is terminated, quit whole program
wxWindow *win = wxTheApp->GetTopWindow(); wxWindow *win = wxTheApp->GetTopWindow();
if ( win ) if ( win )
win->Destroy(); win->Destroy();
return TRUE; // delete self
the_connection = NULL;
return wxConnection::OnDisconnect();
} }

View File

@@ -37,8 +37,6 @@ private:
class MyConnection: public wxConnection class MyConnection: public wxConnection
{ {
public: public:
MyConnection();
bool OnAdvise(const wxString& topic, const wxString& item, char *data, int size, wxIPCFormat format); bool OnAdvise(const wxString& topic, const wxString& item, char *data, int size, wxIPCFormat format);
bool OnDisconnect(); bool OnDisconnect();
}; };

View File

@@ -57,7 +57,6 @@ END_EVENT_TABLE()
// global variables // global variables
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
char ipc_buffer[4000];
MyConnection *the_connection = NULL; MyConnection *the_connection = NULL;
// ============================================================================ // ============================================================================
@@ -134,6 +133,9 @@ void MyFrame::OnListBoxClick(wxCommandEvent& WXUNUSED(event))
if (listBox) if (listBox)
{ {
wxString value = listBox->GetStringSelection(); wxString value = listBox->GetStringSelection();
/* Because the_connection only holds one connection, in this sample only
one connection can receive advise messages */
if (the_connection) if (the_connection)
{ {
the_connection->Advise(IPC_ADVISE_NAME, (wxChar *)value.c_str()); the_connection->Advise(IPC_ADVISE_NAME, (wxChar *)value.c_str());
@@ -161,6 +163,14 @@ IPCDialogBox::IPCDialogBox(wxWindow *parent, const wxString& title,
Fit(); Fit();
} }
IPCDialogBox::~IPCDialogBox( )
{
// wxWindows exit code destroys dialog before destroying the connection in
// OnExit, so make sure connection won't try to delete the dialog later.
if (m_connection)
m_connection->dialog = NULL;
}
void IPCDialogBox::OnQuit(wxCommandEvent& event) void IPCDialogBox::OnQuit(wxCommandEvent& event)
{ {
m_connection->Disconnect(); m_connection->Disconnect();
@@ -174,7 +184,7 @@ void IPCDialogBox::OnQuit(wxCommandEvent& event)
wxConnectionBase *MyServer::OnAcceptConnection(const wxString& topic) wxConnectionBase *MyServer::OnAcceptConnection(const wxString& topic)
{ {
if ( topic == IPC_TOPIC ) if ( topic == IPC_TOPIC )
return new MyConnection(ipc_buffer, WXSIZEOF(ipc_buffer)); return new MyConnection();
// unknown topic // unknown topic
return NULL; return NULL;
@@ -184,8 +194,8 @@ wxConnectionBase *MyServer::OnAcceptConnection(const wxString& topic)
// MyConnection // MyConnection
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
MyConnection::MyConnection(char *buf, int size) MyConnection::MyConnection()
: wxConnection(buf, size) : wxConnection()
{ {
dialog = new IPCDialogBox(wxTheApp->GetTopWindow(), "Connection", dialog = new IPCDialogBox(wxTheApp->GetTopWindow(), "Connection",
wxPoint(100, 100), wxSize(500, 500), this); wxPoint(100, 100), wxSize(500, 500), this);
@@ -197,7 +207,11 @@ MyConnection::~MyConnection()
{ {
if (the_connection) if (the_connection)
{ {
if (dialog)
{
dialog->m_connection = NULL;
dialog->Destroy(); dialog->Destroy();
}
the_connection = NULL; the_connection = NULL;
} }
} }

View File

@@ -42,7 +42,7 @@ class IPCDialogBox;
class MyConnection : public wxConnection class MyConnection : public wxConnection
{ {
public: public:
MyConnection(char *buf, int size); MyConnection();
~MyConnection(); ~MyConnection();
bool OnExecute(const wxString& topic, char *data, int size, wxIPCFormat format); bool OnExecute(const wxString& topic, char *data, int size, wxIPCFormat format);
@@ -50,7 +50,6 @@ public:
bool OnPoke(const wxString& topic, const wxString& item, char *data, int size, wxIPCFormat format); bool OnPoke(const wxString& topic, const wxString& item, char *data, int size, wxIPCFormat format);
bool OnStartAdvise(const wxString& topic, const wxString& item); bool OnStartAdvise(const wxString& topic, const wxString& item);
private:
IPCDialogBox *dialog; IPCDialogBox *dialog;
}; };
@@ -68,10 +67,10 @@ public:
const wxPoint& pos, const wxPoint& pos,
const wxSize& size, const wxSize& size,
MyConnection *the_connection); MyConnection *the_connection);
~IPCDialogBox( );
void OnQuit(wxCommandEvent& event); void OnQuit(wxCommandEvent& event);
private:
MyConnection *m_connection; MyConnection *m_connection;
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()

View File

@@ -133,8 +133,6 @@ static wxList wxAtomTable(wxKEY_STRING);
static wxList wxDDEClientObjects; static wxList wxDDEClientObjects;
static wxList wxDDEServerObjects; static wxList wxDDEServerObjects;
char *DDEDefaultIPCBuffer = NULL;
int DDEDefaultIPCBufferSize = 0;
static bool DDEInitialized = FALSE; static bool DDEInitialized = FALSE;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -198,8 +196,6 @@ void wxDDECleanUp()
DdeUninitialize(DDEIdInst); DdeUninitialize(DDEIdInst);
DDEIdInst = 0; DDEIdInst = 0;
} }
delete [] DDEDefaultIPCBuffer;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -314,6 +310,7 @@ wxDDEServer::~wxDDEServer()
{ {
wxDDEConnection *connection = (wxDDEConnection *)node->Data(); wxDDEConnection *connection = (wxDDEConnection *)node->Data();
wxNode *next = node->Next(); wxNode *next = node->Next();
connection->SetConnected(false);
connection->OnDisconnect(); // May delete the node implicitly connection->OnDisconnect(); // May delete the node implicitly
node = next; node = next;
} }
@@ -463,20 +460,8 @@ bool wxDDEClient::DeleteConnection(WXHCONV conv)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
wxDDEConnection::wxDDEConnection(char *buffer, int size) wxDDEConnection::wxDDEConnection(char *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_client = NULL; m_client = NULL;
m_server = NULL; m_server = NULL;
@@ -485,20 +470,17 @@ wxDDEConnection::wxDDEConnection(char *buffer, int size)
} }
wxDDEConnection::wxDDEConnection() wxDDEConnection::wxDDEConnection()
: wxConnectionBase()
{ {
m_hConv = 0; m_hConv = 0;
m_sendingData = NULL; m_sendingData = NULL;
m_server = NULL; m_server = NULL;
m_client = NULL; m_client = NULL;
if (DDEDefaultIPCBuffer == NULL)
DDEDefaultIPCBuffer = new char[DDEDefaultIPCBufferSize];
m_bufPtr = DDEDefaultIPCBuffer;
m_bufSize = DDEDefaultIPCBufferSize;
} }
wxDDEConnection::~wxDDEConnection() wxDDEConnection::~wxDDEConnection()
{ {
Disconnect();
if (m_server) if (m_server)
m_server->GetConnections().DeleteObject(this); m_server->GetConnections().DeleteObject(this);
else else
@@ -508,6 +490,9 @@ wxDDEConnection::~wxDDEConnection()
// Calls that CLIENT can make // Calls that CLIENT can make
bool wxDDEConnection::Disconnect() bool wxDDEConnection::Disconnect()
{ {
if ( !GetConnected() )
return true;
DDEDeleteConnection(GetHConv()); DDEDeleteConnection(GetHConv());
bool ok = DdeDisconnect(GetHConv()) != 0; bool ok = DdeDisconnect(GetHConv()) != 0;
@@ -516,6 +501,8 @@ bool wxDDEConnection::Disconnect()
DDELogError(_T("Failed to disconnect from DDE server gracefully")); DDELogError(_T("Failed to disconnect from DDE server gracefully"));
} }
SetConnected( false ); // so we don't try and disconnect again
return ok; return ok;
} }
@@ -546,6 +533,7 @@ bool wxDDEConnection::Execute(const wxChar *data, int size, wxIPCFormat format)
char *wxDDEConnection::Request(const wxString& item, int *size, wxIPCFormat format) char *wxDDEConnection::Request(const wxString& item, int *size, wxIPCFormat format)
{ {
DWORD result; DWORD result;
HSZ atom = DDEGetAtom(item); HSZ atom = DDEGetAtom(item);
HDDEDATA returned_data = DdeClientTransaction(NULL, 0, HDDEDATA returned_data = DdeClientTransaction(NULL, 0,
@@ -561,14 +549,19 @@ char *wxDDEConnection::Request(const wxString& item, int *size, wxIPCFormat form
return NULL; return NULL;
} }
DWORD len = DdeGetData(returned_data, (LPBYTE)m_bufPtr, m_bufSize, 0); DWORD len = DdeGetData(returned_data, NULL, 0, 0);
wxChar *data = GetBufferAtLeast( len );
wxASSERT_MSG(data != NULL,
_T("Buffer too small in wxDDEConnection::Request") );
DdeGetData(returned_data, (LPBYTE)data, len, 0);
DdeFreeDataHandle(returned_data); DdeFreeDataHandle(returned_data);
if (size) if (size)
*size = (int)len; *size = (int)len;
return m_bufPtr; return data;
} }
bool wxDDEConnection::Poke(const wxString& item, wxChar *data, int size, wxIPCFormat format) bool wxDDEConnection::Poke(const wxString& item, wxChar *data, int size, wxIPCFormat format)
@@ -645,7 +638,7 @@ bool wxDDEConnection::Advise(const wxString& item,
HSZ item_atom = DDEGetAtom(item); HSZ item_atom = DDEGetAtom(item);
HSZ topic_atom = DDEGetAtom(m_topicName); HSZ topic_atom = DDEGetAtom(m_topicName);
m_sendingData = data; m_sendingData = data; // mrf: potential for scope problems here?
m_dataSize = size; m_dataSize = size;
m_dataType = format; m_dataType = format;
@@ -718,11 +711,15 @@ _DDECallback(WORD wType,
case XTYP_DISCONNECT: case XTYP_DISCONNECT:
{ {
wxDDEConnection *connection = DDEFindConnection(hConv); wxDDEConnection *connection = DDEFindConnection(hConv);
if (connection && connection->OnDisconnect()) if (connection)
{
connection->SetConnected( false );
if (connection->OnDisconnect())
{ {
DDEDeleteConnection(hConv); // Delete mapping: hConv => connection DDEDeleteConnection(hConv); // Delete mapping: hConv => connection
return (DDERETURN)(DWORD)TRUE; return (DDERETURN)(DWORD)TRUE;
} }
}
break; break;
} }
@@ -732,13 +729,18 @@ _DDECallback(WORD wType,
if (connection) if (connection)
{ {
DWORD len = DdeGetData(hData, DWORD len = DdeGetData(hData, NULL, 0, 0);
(LPBYTE)connection->m_bufPtr,
connection->m_bufSize, wxChar *data = connection->GetBufferAtLeast( len );
0); wxASSERT_MSG(data != NULL,
_T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
DdeGetData(hData, (LPBYTE)data, len, 0);
DdeFreeDataHandle(hData); DdeFreeDataHandle(hData);
if ( connection->OnExecute(connection->m_topicName, if ( connection->OnExecute(connection->m_topicName,
connection->m_bufPtr, data,
(int)len, (int)len,
(wxIPCFormat) wFmt) ) (wxIPCFormat) wFmt) )
{ {
@@ -788,15 +790,19 @@ _DDECallback(WORD wType,
{ {
wxString item_name = DDEStringFromAtom(hsz2); wxString item_name = DDEStringFromAtom(hsz2);
DWORD len = DdeGetData(hData, DWORD len = DdeGetData(hData, NULL, 0, 0);
(LPBYTE)connection->m_bufPtr,
connection->m_bufSize, wxChar *data = connection->GetBufferAtLeast( len );
0); wxASSERT_MSG(data != NULL,
_T("Buffer too small in _DDECallback (XTYP_EXECUTE)") );
DdeGetData(hData, (LPBYTE)data, len, 0);
DdeFreeDataHandle(hData); DdeFreeDataHandle(hData);
connection->OnPoke(connection->m_topicName, connection->OnPoke(connection->m_topicName,
item_name, item_name,
(wxChar*)connection->m_bufPtr, data,
(int)len, (int)len,
(wxIPCFormat) wFmt); (wxIPCFormat) wFmt);
@@ -871,14 +877,18 @@ _DDECallback(WORD wType,
{ {
wxString item_name = DDEStringFromAtom(hsz2); wxString item_name = DDEStringFromAtom(hsz2);
DWORD len = DdeGetData(hData, DWORD len = DdeGetData(hData, NULL, 0, 0);
(LPBYTE)connection->m_bufPtr,
connection->m_bufSize, wxChar *data = connection->GetBufferAtLeast( len );
0); wxASSERT_MSG(data != NULL,
_T("Buffer too small in _DDECallback (XTYP_ADVDATA)") );
DdeGetData(hData, (LPBYTE)data, len, 0);
DdeFreeDataHandle(hData); DdeFreeDataHandle(hData);
if ( connection->OnAdvise(connection->m_topicName, if ( connection->OnAdvise(connection->m_topicName,
item_name, item_name,
connection->m_bufPtr, data,
(int)len, (int)len,
(wxIPCFormat) wFmt) ) (wxIPCFormat) wFmt) )
{ {