I need some guidance on how to implement a C language interface to an MFC DLL that has a message pump handling window messages. The MFC DLL is derived from public CWinApp
and is using classes derived from CAsyncSocket
to handle a TCP/IP connection to a remote application. How should the access to the CWinApp
object be done so that C callable functions exported by the MFC DLL can access the functionality of the CWinApp
object in a threadsafe and Windows compliant way?
This is a complex, multi-threaded Windows application composed of several DLLs with a mixture of C and C++. Several of the DLLs are MFC based DLLs and the main exe is a dialog based MFC application. When doing a shutdown of the application we send a WM_QUIT
message to the main window handle using PostMessage()
and in most cases the application will shutdown and exit.
The various threads are started using the _beginthreadex()
function.
However in some cases we are seeing that the application hangs as in the various windows are still displayed but the application is no longer responsive. Attaching to the process using Visual Studio and then doing a Break All from the Debug menu a warning dialog appears saying "The process appears to be deadlocked (or is not running any user-mode code). All threads have been stopped.".
Looking in the Threads display, most of the threads seem to be at location 0x7c90e514
which appears to be the result of a call to WaitForSingleObject()
. We are using WaitForSingleObject()
when doing a semaphore request as part of using the Windows semaphore API. There are some threads whose origin I am not certain though they may be from a couple of COM objects we are using in another MFC DLL.
We have also noticed that sometimes when the application shuts down a command shell window suddenly appears stays displayed for a varying number of seconds and then disappears. We do not always see this behavior. The application does not hang when we see the window displayed. I would be interested in a possible explanation for this behavior.
However one thread that is a Windows MFC based DLL is within a function _AfxActivationWndProc()
which appears to be a handler for window message processing. When the Break All is done from the Debug menu, it appears that this thread is the current thread however I believe it is suspended (1 in the Suspend column of the Threads window of the debugger). The call stack shows that this function is invoked through a call chain from CCmdTarget::InternalRelease().
What I believe is the problem is that under some circumstances the message pump for some thread is being blocked by the call to WaitForSingleObject()
and a window message is arriving with the message pump not pumping and the MFC DLL that is trying to finalize and do the CCmdTarget::InternalRelease()
is blocked from finishing due to a reference count not being properly decremented.
The particular MFC DLL that seems to be hanging is using classes derived from the CAsyncSocket
MFC class to implement an interface for a remote application to send and receive data with this application. The MFC DLL exposes a set of functions with a C interface. An example of such a function is the following which is called by the main application to provide for the mechanics of the MFC DLL sending asynchronous messages to the main application window as messages arrive over the TCP/IP connection from the remote application:
CONNENGINE_API int fnConnEngineSetWindowHandleExt (CONNENGINEHANDLE hConnEngineSocket, HWND hWinHandle, UINT wReceiveMsgId, UINT wSocketCloseMsgId)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
theApp.m_hWinHandle = hWinHandle;
theApp.m_wReceiveMsgId = wReceiveMsgId;
theApp.m_wSocketCloseMsgId = wSocketCloseMsgId;
if (theApp.m_ListenSocket != 0) {
theApp.m_ListenSocket->SetWindowHandleExt (hWinHandle, wReceiveMsgId, wSocketCloseMsgId);
}
return 0;
}
The CONNENGINE_API
is a defined constant within an include file that looks like:
#ifdef CONNENGINE_EXPORTS
#define CONNENGINE_API __declspec(dllexport)
#else
#define CONNENGINE_API __declspec(dllimport)
#endif
And we have this in an include file where the function declarations are using the extern "C"
construct with guards to allow inclusion in both C and C++ source files.
#if defined(__cplusplus)
extern "C" {
#endif
// function declarations
CONNENGINE_API int fnConnEngineSetWindowHandleExt (CONNENGINEHANDLE hConnEngineSocket, HWND hWinHandle, UINT m_wReceiveMsgId, UINT m_wSocketCloseMsgId);
#if defined(__cplusplus)
};
#endif
The file is included into the MFC DLL source file with the following statements so that the DLL is exporting the functions.
#define CONNENGINE_EXPORTS
#include "ConnEngine.h"
The MFC DLL creates a listen
socket to wait for a connection request and when it comes in, creates a worker socket to handle the actual I/O. There is only one connection at a time.
There are other threads that are also using an MFC based DLL which have embedded COM or Active-X controls for secure transaction processing as well as device interfaces. The MFC based DLLs containing the COM objects use a similar interface technique.
My primary question is how should an MFC DLL that is using a Windows message pump to process windows message for its functionality export C callable functions that will be used by a thread that is started using _beginthreadex()
? How would an MFC DLL hosting COM objects export functionality?
Documentation links
Difference between Afxbeginthread and CreateThread
How to exit Win32 Application via API
What does AFX_MANAGE_STATE(AfxGetStaticModuleState()) do exactly