|
-
November 14th, 2005, 03:57 PM
#1
Handling messages in threaded MFC Dlls
Hi ...
I have been trying to implement some message handling in MFC dlls for a couple days. I have read some good info on CodeGuru site. Many times there is the lack a full conclusion. My message issues were also in a thread, which complicated things a bit. I have decided that I would post the solution to problem below on the site. I hope it will help the next developer with similar issues.
The problem.
I have an MFC application (SDI) and two MFC dlls. One dll (MxTcpComm.dll) is listening on a socket then sending a message when it gets a connection/data. The other dll will handle the message (MxProcess.dll). Both dlls are threaded. Originally I was sending the message back to the application (Test.exe) and handling it in the MainFrame Class with ON_MESSAGE(WM_THREADED_SERVER_PROCESS, OnMessageThreadedServer).
The biggest problem was moving the message handling from Test.exe to MxProcess.dll. I attempted to create a hidden window in the Constructor of the CMxProcessControlManage Class. The message handling code using ::GetMessage() was inside of a thread started from CMxProcessControlManage Class. This did not work as the thread will have its own message loop. To make this work I needed to move the code to create the hidden window into the thread where the messages are being handled.
Below are the code snippets for each component.
Code:
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
Test.exe
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
BOOL CTestApp::InitInstance()
{
// If we have enabled the peer-peer process control then we will
// start the threads.
if (IsEnablePeerToPeer())
{
//AfxMessageBox("Note: A new Process Control Manager is in place and not yet operational.");
CMxProcessControlManage *pMxProcessControlManage;
pMxProcessControlManage = new CMxProcessControlManage();
// Set the values
pMxProcessControlManage->SetLog(GetLog());
pMxProcessControlManage->SetOwner(GetOwner());
pMxProcessControlManage->SetAppIp(GetProcessControlAppIp());
pMxProcessControlManage->SetAppPort(GetProcessControlAppPort());
pMxProcessControlManage->SetPeerIp(GetProcessControlPeerIp());
pMxProcessControlManage->SetPeerPort(GetProcessControlPeerPort());
// Start the process
pMxProcessControlManage->Start();
}
}
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
MxProcess.dll
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
CMxProcessControlManage::CMxProcessControlManage()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
LoadRegistrySettings();
m_pOwner = 0x00;
SetThreadCount(0);
SetThreadInit(false);
SetThreadAlive(false);
// *** This will not handle messages in the thread. We needed to
// *** move this inside the thread
m_pWnd = new CWnd;
m_pWnd->Create(NULL, // Class name
"hidden window", // Window name
WS_OVERLAPPEDWINDOW, // Style
CRect(0,0,10,10), // Size and position
CWnd::GetDesktopWindow(), // Parent window
0); // ID of child window
}
int CMxProcessControlManage::Start(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
if (!(m_ThreadPrimary = AfxBeginThread(ThreadProcessControlManager, this)))
{
nReturn = -1;
}
return nReturn;
}
UINT CMxProcessControlManage::ThreadProcessControlManager(LPVOID pParam)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
int nThreadCount = 0;
int nIndex = 0;
int nRowCount = 0;
MSG *pMsg = NULL;
bool bMessageLoop = true;
unsigned int nTimerId1 = 0;
CMxProcessControlManage* pProcessControlManage = (CMxProcessControlManage*)pParam;
// Create the hidden window that will be used to handle messages in this
// thread
CWnd *pWnd = new CWnd;
pWnd->Create(NULL, // Class name
"hidden window", // Window name
WS_OVERLAPPEDWINDOW, // Style
CRect(0,0,10,10), // Size and position
CWnd::GetDesktopWindow(), // Parent window
0); // ID of child window
pProcessControlManage->SetOwner(pWnd);
// Start the process control listening thread.
if (!(pProcessControlManage->m_ThreadListener = AfxBeginThread(ThreadProcessControlListener, pProcessControlManage)))
{
csLogMessage.Format("Process control listener thread failing to start!");
pProcessControlManage->m_pLog->Write(MXLOG_SEVERITY_ERROR, csLogMessage, csMethodName);
}
// Begin listening for messages from listing socket.
pMsg = new MSG;
// Start the program control timer. This timer will signal that
// a message should be sent to the server
nTimerId1 = ::SetTimer(NULL, NULL, 92500, NULL);
while (bMessageLoop)
{
// Get messages from the hidden window message loop
::GetMessage(pMsg, pProcessControlManage->GetOwner()->m_hWnd, 0, 0);
switch (pMsg->message)
{
case WM_TIMER:
if (pMsg->wParam == nTimerId1)
{
csLogMessage.Format("Message loop timeout exceeded.");
pProcessControlManage->m_pLog->Write(MXLOG_SEVERITY_INFO, csLogMessage, csMethodName);
bMessageLoop = false;
DispatchMessage(pMsg);
}
break;
case WM_THREADED_SERVER_PROCESS:
csLogMessage.Format("Message: WM_THREADED_SERVER_PROCESS.");
pProcessControlManage->m_pLog->Write(MXLOG_SEVERITY_INFO, csLogMessage, csMethodName);
DispatchMessage(pMsg);
break;
default:
DispatchMessage(pMsg);
break;
}
}
// delete the message object
delete pMsg;
// delete the hidden window
delete pWnd;
return 0;
}
UINT CMxProcessControlManage::ThreadProcessControlListener(LPVOID pParam)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
if ((nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0)
{
csLogMessage.Format("WSAStartup() returned error code: %d. Thread:%d",
nCode,
nThreadCount);
pProcessControlManage->m_pLog->Write(MXLOG_SEVERITY_ERROR, csLogMessage, csMethodName);
}
else
{
csLogMessage.Format("Winsock started. Thread:%d", nThreadCount);
pProcessControlManage->m_pLog->Write(MXLOG_SEVERITY_INFO, csLogMessage, csMethodName);
csLogMessage.Format("Create the pThreaderServer object. Thread:%d", nThreadCount);
pProcessControlManage->m_pLog->Write(MXLOG_SEVERITY_INFO, csLogMessage, csMethodName);
// Create the pMxThreaderServer object
CMxThreadedServer *pMxThreaderServer;
pMxThreaderServer = new CMxThreadedServer();
pMxThreaderServer->SetOwner(pProcessControlManage->GetOwner());
// This should block here forever !!
csLogMessage.Format("Starting pThreaderServer object. Thread:%d", nThreadCount);
pProcessControlManage->m_pLog->Write(MXLOG_SEVERITY_INFO, csLogMessage, csMethodName);
pMxThreaderServer->Start(pProcessControlManage->GetAppIp(),
pProcessControlManage->GetAppPort());
// Shut Winsock back down and take off.
WSACleanup();
}
// Ending message
csLogMessage.Format("Ending listening thread. Thread:%d", nThreadCount);
pProcessControlManage->m_pLog->Write(MXLOG_SEVERITY_INFO, csLogMessage, csMethodName);
return 0;
}
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
MxTcpComm.dll
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
int CMxThreadedServer::Start(const char* pcAddress, int nPort)
{
SetUpListener(pcAddress, htons(nPort));
if (m_sListen == INVALID_SOCKET)
{
return 3;
}
// Waiting for connections
while (1)
{
AcceptConnection();
}
return 0;
}
void CMxThreadedServer::AcceptConnection(void)
{
sockaddr_in sinRemote;
int nAddrSize = sizeof(sinRemote);
while (1)
{
m_sAccept = accept(m_sListen,
(sockaddr*)&sinRemote,
&nAddrSize);
if (m_sAccept != INVALID_SOCKET)
{
if (!(m_ThreadReceiveHandler = AfxBeginThread(ThreadReceiveHandler, this)))
{
// ThreadReceiveHandler thread failed to start!
}
}
else
{
// handle WSA errors
return;
}
}
}
UINT CMxThreadedServer::ThreadReceiveHandler(LPVOID pParam)
{
int nRetval = 0;
// Cast the void pointer passed to the thread back to caller
CMxThreadedServer *pMxThreadedServer = (CMxThreadedServer*)pParam;
if (!pMxThreadedServer->ReceiveIncomingPackets())
{
// handle WSA errors
nRetval = 3;
}
// Shutting connection down.
if (pMxThreadedServer->ShutdownConnection())
{
// connection down
}
else
{
// handle WSA errors
nRetval = 3;
}
return nRetval;
}
bool CMxThreadedServer::ReceiveIncomingPackets(void)
{
// Read data from client
char acReadBuffer[1024];
int nReadBytes;
do
{
// Get the data from the socket
nReadBytes = recv(m_sAccept, acReadBuffer, 1024, 0);
// Check the bytes received.
if (nReadBytes > 0)
{
csTemp = (CString)acReadBuffer;
csTemp = csTemp.Left(nReadBytes);
SetReceiveData(csTemp);
if (GetOwner())
{
// Send a message that we are done
::PostMessage(GetOwner()->m_hWnd,
WM_THREADED_SERVER_PROCESS,
(WPARAM) MXTS_MESSAGE_DATA_RECEIVED,
(LPARAM) nReadBytes);
}
}
else if (nReadBytes == SOCKET_ERROR)
{
return false;
}
}
while (nReadBytes != 0);
cout << "Connection closed by peer." << endl;
return true;
}
The End
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|