I'm working on a project that is using wxWidgets sockets in a DLL and I'm getting odd behaviour. I'll go through the problem from the top and drill down. I apologize if I've provided too much (or too little) information; I've tried to prune down the excess.
First, the odd behaviour:
The server is created, and my client connects to it.
I find this odd because I never get the connection attempt event to call Accept() on, so I would expect that the client wouldn't be able to claim it is fully connected. I also don't get any other events - connection, input, output, lost, etc.
Second, the restrictions: I have no control over the application using this library, and I can't block on the main thread for the event loop.
I've set up the event table, used connect, and copied the wx initialization code from a DLL project that creates windows (so the event framework must work with that project).
Basic project structure:
There's a main class that gets created (Project) that also has the wx init and dll functions. It creates a serverconnection class that subclasses connection (connection is the event handler and serverconnection re-implements some of the functions that the (not shown) clientConnection class would do differently.
project.h looks similar to (in addition to exporting the DLL functions) :
class Project
{
public:
~Project();
static Project* getInstance();
void run();
void stop();
static bool m_keepGoing;
private:
Project();
void ExecuteCommandPacket(Packet& packet);
static Project* m_instance;
ServerConnection * m_server;
ServerConnectionHandler * m_serverHandler;
};
While the project.cpp looks similar to:
#include "wx/wx.h"
#include "wx/msw/private.h"
#if WIN32
#define snprintf sprintf_s
#define strncpy strcpy_s
#define strncat strncat_s
#endif
#define PORT 4378 // port we're listening on
using namespace std;
Logger gLogger;
class wxDLLApp : public wxApp
{
bool OnInit();
};
bool wxDLLApp::OnInit()
{
return true;
}
IMPLEMENT_APP_NO_MAIN(wxDLLApp)
Project::Project()
{
// Set up logger's output file
TCHAR fullLogName[_MAX_PATH];
const TCHAR const * logName = "/Project.log";
GetEnvironmentVariable("userprofile", fullLogName, _MAX_PATH);
strncat(fullLogName, _MAX_PATH, logName, _MAX_PATH - strlen(logName));
gLogger.setFile(fullLogName);
}
Project* Project::getInstance()
{
if (m_instance == NULL)
{
m_instance = new Project();
}
return m_instance;
}
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
wxApp::SetInstance(new wxDLLApp());
wxEntry(GetModuleHandle(NULL),NULL,NULL,SW_SHOW);
return true;
}
// DllMain
BOOL WINAPI DllMain(HINSTANCE hDll, DWORD fdwReason, LPVOID lpvReserved)
{
// GHDll = hDll;
int argc = 0;
char **argv = NULL;
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
//use wxInitialize() if you don't want GUI instead of the following 12 lines
wxInitialize();
// tried both
/*
wxSetInstance((HINSTANCE)hDll);
wxEntryStart(argc, argv);
if ( !wxTheApp || !wxTheApp->CallOnInit() )
return FALSE;
*/
Project::getInstance()->run();
gLogger.logString(Logger::LOG_DEBUG, "Called DllMain with DLL_PROCESS_ATTACH");
break;
case DLL_PROCESS_DETACH:
gLogger.logString(Logger::LOG_DEBUG, "Called DllMain with DLL_PROCESS_DETACH");
wxUninitialize();
break;
case DLL_THREAD_ATTACH:
gLogger.logString(Logger::LOG_DEBUG, "Called DllMain with DLL_THREAD_ATTACH");
break;
case DLL_THREAD_DETACH:
gLogger.logString(Logger::LOG_DEBUG, "Called DllMain with DLL_THREAD_DETACH");
break;
}
return TRUE;
}
void Project::run()
{
m_serverHandler = new ServerConnectionHandler();
m_server = new ServerConnection(m_serverHandler);
}
I then have a Connection class defined as something like:
#ifndef CONNECTION_H
#define CONNECTION_H
#include <list>
#include <wx/event.h>
#include <wx/socket.h>
class ClientConnectionHandler;
class wxSocketBase;
enum
{
SERVER_ID = 100,
SOCKET_ID
};
class Connection : public wxEvtHandler
{
public:
Connection();
void OnSocketEvent(wxSocketEvent &event);
virtual void OnServerEvent(wxSocketEvent &event);
protected:
virtual void disconnect();
void connectBaseFunctionality();
ClientConnectionHandler* m_handler;
wxSocketBase* m_socket;
private:
DECLARE_EVENT_TABLE()
};
#endif//CONNECTION_H
And the connection.cpp has (among other parts):
BEGIN_EVENT_TABLE(Connection, wxEvtHandler)
EVT_SOCKET(SOCKET_ID, Connection::OnSocketEvent)
END_EVENT_TABLE()
void Connection::connectBaseFunctionality()
{
gLogger.logString(Logger::LOG_INFO, "Connection connecting base functionality");
//m_socket->SetEventHandler(*this, SOCKET_ID);
m_socket->SetEventHandler(*this, wxSOCKET_INPUT);
m_socket->SetEventHandler(*this, wxSOCKET_OUTPUT);
m_socket->SetEventHandler(*this, wxSOCKET_CONNECTION);
m_socket->SetEventHandler(*this, wxSOCKET_LOST);
m_socket->SetNotify(wxSOCKET_INPUT_FLAG|wxSOCKET_OUTPUT_FLAG|wxSOCKET_CONNECTION_FLAG|wxSOCKET_LOST_FLAG);
m_socket->Notify(true);
// this does nothing
Connect(SOCKET_ID, wxSOCKET_CONNECTION, wxObjectEventFunction(&Connection::OnSocketEvent));
/* This doesn't help
Connect(wxSOCKET_INPUT, wxEvtHandler(&Connection::OnSocketEvent), 0, this);
Connect(wxSOCKET_OUTPUT, wxEvtHandler(&Connection::OnSocketEvent), 0, this);
Connect(wxSOCKET_CONNECTION, wxEvtHandler(&Connection::OnSocketEvent), 0, this);
Connect(wxSOCKET_LOST, wxEvtHandler(&Connection::OnSocketEvent), 0, this);
*/
/* Nor does this
void (*socketEvtHandler)(wxSocketEvent &);
socketEvtHandler = &(this->Connection::OnSocketEvent);
EVT_SOCKET(wxSOCKET_INPUT, socketEvtHandler);
EVT_SOCKET(wxSOCKET_OUTPUT, socketEvtHandler);
EVT_SOCKET(wxSOCKET_CONNECTION, socketEvtHandler);
EVT_SOCKET(wxSOCKET_LOST, socketEvtHandler);
*/
// or these
//m_socket->SetEventHandler((*this), wxSOCKET_INPUT);
//m_socket->SetEventHandler((*this), wxSOCKET_INPUT);
//Connect(wxSOCKET_INPUT, wxSocketEvtHandler(&wxConnection::readyRead), 0, this);
}
void Connection::OnSocketEvent(wxSocketEvent &event)
{
gLogger.logString(Logger::LOG_INFO, "Connection Received Event");
switch (event.GetSocketEvent())
{
case wxSOCKET_CONNECTION:
{
OnServerEvent(event);
break;
}
case wxSOCKET_INPUT:
{
readyRead(event);
break;
}
case wxSOCKET_LOST:
{
socketDropped(event);
break;
}
}
}
void Connection::OnServerEvent(wxSocketEvent &event)
{
// This code should never be executed - either the virtual table lookup redirects to the ServerConnection's overriding one, or it's not a server and we won't get the server events
gLogger.logString(Logger::LOG_CRITICAL, "CONNECTION::OnServerEvent - THIS CODE SHOULD NEVER BE EXECUTED");
}
With a subclass that looks like this:
#ifndef SERVERCONNECTION_H
#define SERVERCONNECTION_H
#include "Connection.h"
class ServerConnectionHandler;
class ServerConnection : public Connection
{
public:
ServerConnection(ServerConnectionHandler* handler);
void setConnectionHandler(ServerConnectionHandler * handler);
protected:
void OnServerEvent(wxSocketEvent &event);
ServerConnectionHandler* m_serverHandler;
};
#endif//WXSERVERCONNECTION_H
And has:
#include "ServerConnection.h"
#include "ClientConnection.h"
#include "ServerConnectionHandler.h"
#include "Logger.h"
#include "wx\socket.h"
extern Logger gLogger;
ServerConnection::ServerConnection(ServerConnectionHandler* handler)
{
wxIPV4address address;
address.AnyAddress();
address.Service(this->m_port);
m_socket = new wxSocketServer(address, wxSOCKET_REUSEADDR);
if (m_socket->IsOk())
{
gLogger.logString(Logger::LOG_INFO, "Server listening");
// sets up events so we can do accept()s only when someone tries to connect
connectBaseFunctionality();
}
else
{
gLogger.logString(Logger::LOG_CRITICAL, "Server cannot be initialized");
}
setConnectionHandler(handler);
}
void ServerConnection::setConnectionHandler(ServerConnectionHandler* handler)
{
Connection::m_handler = handler;
m_serverHandler = handler;
}
void ServerConnection::OnServerEvent(wxSocketEvent &event)
{
gLogger.logString(Logger::LOG_INFO, "Server - Received Connection");
// event new client connection and pass it back to the handler
wxSocketBase * socketClient = ((wxSocketServer *)m_socket)->Accept(true);
ClientConnection * clientConnection = new ClientConnection(((wxSocketClient*)socketClient), m_serverHandler);
m_serverHandler->newConnection(clientConnection);
}
And the typical log output after running, connecting, disconnecting and closing is:
Thu Jan 14 16:56:42 2010: Server listening
Thu Jan 14 16:56:42 2010: Connection connecting base functionality
Thu Jan 14 16:56:42 2010: Called DllMain with DLL_PROCESS_ATTACH
Thu Jan 14 16:56:42 2010: Called DllMain with DLL_THREAD_ATTACH
Thu Jan 14 16:56:42 2010: Called DllMain with DLL_THREAD_ATTACH
Thu Jan 14 16:56:43 2010: Called DllMain with DLL_THREAD_ATTACH
Thu Jan 14 16:56:49 2010: Called DllMain with DLL_THREAD_ATTACH
Thu Jan 14 16:56:50 2010: Called DllMain with DLL_THREAD_DETACH
Thu Jan 14 16:56:50 2010: Called DllMain with DLL_THREAD_ATTACH
Thu Jan 14 16:57:19 2010: Called DllMain with DLL_THREAD_DETACH
Thu Jan 14 16:57:19 2010: Called DllMain with DLL_THREAD_DETACH
Thu Jan 14 16:57:19 2010: Called DllMain with DLL_THREAD_DETACH
Thu Jan 14 16:57:19 2010: Called DllMain with DLL_THREAD_DETACH
Thu Jan 14 16:57:19 2010: Called DllMain with DLL_THREAD_DETACH
Thu Jan 14 16:57:19 2010: Called DllMain with DLL_THREAD_DETACH
Thu Jan 14 16:57:19 2010: Called DllMain with DLL_THREAD_DETACH
Thu Jan 14 16:57:19 2010: Stopping Application
Thu Jan 14 16:57:19 2010: Called DllMain with DLL_PROCESS_DETACH
So I get absolutely no notification that the events are coming through (or there would be logged output from Connection's OnSocketEvent function, and it never calls Accept in ServerConnection's OnServerEvent, but the client still says it has a full connection.
Does anyone know what I'm doing wrong so that I'm not getting event notifications?