I was asked to look at some code for a friend. (I rightly hesitated due to the MFC and lots of bad code, but he won...)
This is a dialog box based application that uses a CAsyncSocket
.
The problem manifests in some nonstop debugbreaks and other similar things - there are also problem with an MFC ENSURE()
macro - checking the socket for nullness. All of the issues happen deep in MFC.
Some googling showed up possible resource leaks if one uses themes in Vista/XP, but I don't think that is the problem here.
The code is pretty poor based on my couple hours of debugging, but basically it is doing the following:
(When connection is established there is no problem - it is only the case when no connection)
- Calls Connect(server, socket) (on the derived
CAsyncSocket
object) - In the
OnConnect()
we are notified that connection did not work/not connected. - Inside a window timer for the main dialog/app there is a timer. When the timer event/handler is called we check if connected.
- If we have detected that we are not connected (the
OnConnect()
was not good) then we callCAsyncSocket::Close()
, then callCAsyncSocket::Create()
(with no params) then callCAsyncSocket::Connect(server, port)
Note that the initial call to Connect()
had no preceding call to Create()
.
My first real question:
- What is the difference between the two and why is the
Create()
needed? (if I remove that then it no longer crashes, but I also don't connect when I re-establish connectivity)
The general question:
- What exactly is wrong with the design of the code above?
- How should this work in general?
EDIT:
I fixed the code so that all the paths go through calling Create()
then Connect()
.
I still have a problem with an assert in CAsyncSocket::DoCallBack()
- the last line of the code below is asserting:
void PASCAL CAsyncSocket::DoCallBack(WPARAM wParam, LPARAM lParam)
{
if (wParam == 0 && lParam == 0)
return;
// Has the socket be closed - lookup in dead handle list
CAsyncSocket* pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, TRUE);
// If yes ignore message
if (pSocket != NULL)
return;
pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, FALSE);
if (pSocket == NULL)
{
// Must be in the middle of an Accept call
pSocket = CAsyncSocket::LookupHandle(INVALID_SOCKET, FALSE);
ENSURE(pSocket != NULL);
If I step through that then I get the message box: "Encountered an improper argument"
I think (but am not sure) that MFC is trying to call back the socket AFTER I have closed it. It is in a callback method (DoCallback()
) but I already called Close()
on the socket.
So it does look like an MFC problem, unless I am supposed to unsubscribe first.