0

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

Under what conditions is CCmdTarget::OnFinalRelease called

Could you explain STA and MTA?

Community
  • 1
  • 1
Richard Chambers
  • 14,509
  • 3
  • 62
  • 86
  • 1
    The deadlock is probably due to unmarshaled COM object pointers being used outside their apartment, probably an STA. Also, the proper way of posting a `WM_QUIT` message is with `PostQuitMessage`. If you need to do it from another thread, you can send/post a [custom message](https://msdn.microsoft.com/en-us/library/windows/desktop/ms644931(v=vs.85).aspx) that will make the window/dialog procedure call `PostQuitMessage` in its own thread. – acelent May 05 '15 at 18:50
  • @PauloMadeira, I see from this stack overflow why the PostQuitMessage() is preferred http://stackoverflow.com/questions/10451535/how-to-exit-a-threads-message-loop however I am now looking at a different method. Rather than using `PostMessage()` I am trying using the `_exit()` C runtime function which seems to work quite nicely. Any reason to not use the `_exit()` function? – Richard Chambers May 05 '15 at 19:46
  • @PauloMadeira, here is some additional discussion on exiting a Windows application with an answer that says to send a WM_CLOSE to the main window. http://stackoverflow.com/questions/7247601/how-to-exit-win32-application-via-api and this stackoverflow discusses exiting a process in C++ http://stackoverflow.com/questions/915312/different-ways-of-exiting-a-process-in-c However what I would like is some material or a link to some material to understand how the C callable functions that are exported by an MFC DLL should be written. – Richard Chambers May 05 '15 at 20:03
  • 1
    This is an interesting list of bad Windows programming habits http://www.flounder.com/badprogram.htm and it looks like I have some work in front of me. – Richard Chambers May 05 '15 at 22:12
  • There is a series of articles in codeproject.com on Single Threaded Apartment This is to Understanding the COM Single-Threaded Apartment Par 2 http://www.codeproject.com/Articles/9506/Understanding-The-COM-Single-Threaded-Apartment which discusses COM interface pointer marshaling. – Richard Chambers May 06 '15 at 03:10
  • You have too many questions at once, and no source code whatsoever. For the C callable function, you just need to define a static method or global function with the proper calling convention. For the mix of MFC in a thread not started by `AfxBeginThread`, and I suspect that even so you'll be handling windows from other threads, you should delegate/post the work to the thread owning the window you're manipulating. – acelent May 06 '15 at 10:53

0 Answers0