Home Creators Posts Import Register Favorites Logout

Content

I’ve worked with MFC from my first job 20+ years ago using Visual Studio 6, which used mfc42 from memory, to today Visual Studio 2019 using mfc140.  It’s a framework that everybody seems to be using, well in the windows world at least.


What is MFC?

“Microsoft Foundation Class Library is a C++ object-oriented library for developing desktop applications for Windows”

This framework provides the look and feel of what we expect to see in windows applications.  Do you remember applications with the green tick on the buttons for yes/no?  Another framework that really never took off, why?  It didn’t have the same feel as the rest of windows.


Why hasn’t this been Attempted?

It just some down to man power to implement it.  As an example, ntdll has about 1700 exported functions, whereas mfc has 14000+.  So, just from the per numbers it’s a lot of work.

It’s easy enough to just install the VCRuntime or the single mfc dll itself,  though this isn’t the best out of the box experience.


Wine Bugs

The list of Wine bugs has grown overs the years as each new version of MFC is released. From the earliest mfc42 to the latest mfc140.

https://bugs.winehq.org/buglist.cgi?bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=STAGED&bug_status=REOPENED&bug_status=NEEDINFO&bug_status=RESOLVED&component=mfc&list_id=774531&product=Wine&query_format=advanced


The Beginnings

I started out by running a very simple MFC, self built, application. This gave me the starting point.

wine: Unimplemented function mfc140u.dll.?AfxGetModuleState@@YGPAVAFX_MODULE_STATE@@XZ called at address 7B0123B6 (thread 0024), starting debugger...

If you are even seen the error box that appears. The unimplemented function name is unusual because it’s a C++ function, unlike most of other parts of wine (with the exception of the msvcrt*.dll). Its a mangled function name, mean it contains information about it’s return type and parameters.

It’s easy enough to translate the above string into

<code>class AFX_MODULE_STATE * __stdcall AfxGetModuleState(void)</code>

This is the simple part of it all.  The class “AFX_MODULE_STATE” really document anywhere, so this is where the guess work starts.  Just added a temporary structure for a start works until we know exactly what we are going to need.

Next we have class CWinAppEx, this is where things get a little easier to workout what it will look like but still wont be complete for a little while. The MFC application I generated In Visual Studio(VS) help us somewhat. As a note we aren’t allow to look the MFC source, to ensure that we don’t violate the wine conditions to commit patches.

Lets start with the Class Hierarchy

Number are size of the object 32/64 bits

CMFCApplicationApp

CWinAppEx (280, 456)

CWinApp (204, 376)

CWinThread (68, 136)

CCmdTarget (32, 64)

CObject (4, 8)


Without one bit more information this don’t mean much.

I wrote the following classes to help work out what the values really mean.

class test

{

public:

test();

~test();

}

sizeof (test)  gives a size of 1, confused me for a little bit as well.

class test2

{

public:

test2();

virtual ~test2();

}

The sizeof (test2)  gives a size of 4/8 for each build.

Now that I have the right size but Why?  The compiler has allocated a virtual table (single pointer) for the list of functions.  Now that explains CObject.

Next on the list CCmdTarget, MSDN doesn’t have any public attributes for this, only methods, so at this stage we cannot do much with it.

Class CWinThread, MSDN has a list of documented attributes

https://learn.microsoft.com/en-us/cpp/mfc/reference/cwinthread-class?view=msvc-170


m_pMainWnd (32/64)

m_pActivateWnd (36, 72)

m_bAutoDelete (40, 80)

m_hThread (44, 88)

m_nThreadID (48, 96)


Class CWinApp, MSDN has a list of documented attributes

https://learn.microsoft.com/en-us/cpp/mfc/reference/cwinapp-class?view=msvc-170


m_hInstance (68, 136)

m_lpCmdLine (72, 144)

m_nCmdShow (76, 152)

m_pszAppName (80, 168)

m_pszAppID (84, 168)

m_pszRegistryKey (88, 176)

m_bHelpMode (96, 192)

m_pszExeName (100, 200)

m_pszHelpFilePath (104, 208)

m_pszProfileName (108, 216)

m_eHelpType (112, 224)


Class CWinAppEx, MSDN has a list of documented attributes

https://learn.microsoft.com/en-us/cpp/mfc/reference/cwinappex-class?view=msvc-170


m_bForceImageReset (256, 432)


How to get the offsets?

Luckily the compiler has builtin function we can use to get these values “offsetof(CMFCApplication2App, m_pMainWnd)”.

Know this information we can attempt to form a structure.

struct c_winappex
{
/* CObject */
/* - No Variables */

/* CCmdTarget */
void *cmdtarget[8];

/* CWinThread */
void *m_pMainWnd;        /* CWnd* */
void *m_pActiveWnd;      /* CWnd* */
BOOL m_bAutoDelete;
HANDLE m_hThread;
DWORD m_nThreadID;

#ifndef _WIN64
char unk[14];
#else
char unk[32];
#endif

/* CWinApp */
HINSTANCE m_hInstance;
TCHAR *m_lpCmdLine;
   int    m_nCmdShow;
TCHAR *m_pszAppName;
TCHAR *m_pszAppID;
TCHAR *m_pszRegistryKey;

   void *win_app_unk;

BOOL m_bHelpMode;
TCHAR *m_pszExeName;
TCHAR *m_pszHelpFilePath;
TCHAR *m_pszProfileName;
AFX_HELP_TYPE m_eHelpType;

#ifndef _WIN64
   char unk2[56];
#else
   char unk2[104];
#endif

DWORD m_dwRestartManagerSupportFlags;

#ifndef _WIN64
   char unk3[80];
#else
   char unk3[96];
#endif

/* CWinAppEx */
BOOL m_bForceImageReset;

#ifndef _WIN64
   char unk4[16];
#else
   char unk4[14];
#endif
void *vtbl;
};

That the basics of starting an mfc dlls.  It tool 7 patches to stop the crash of my sample project but will take a lot more to make it usable.  With the amount of time it took to get it to this stage, I really don’t want to think of how much more time it will take.  One patch at a time.


At some point, wine-staging will properly end up with this patchset but it will need to do something useful.  More than just stop a crash.


Comments

bla laber

The interesting thing is the tipping point, at which percentage of possible functions a significant portion of applications starts working

winestaging

I'm still working through this. On initial look, is only a drop in the ocean of the patches/investigations required. Whats the expression, "Build it, and they will come".