воскресенье, 10 февраля 2013 г.

MFC in desktop Windows RT apps

As we can build desktop Windows RT applications now - it is a good idea to use MFC for GUI. But suddenly I've found that there is no MFC 11.0 for ARM in Visual Studio 2012, even with the ARM WDK installed! The program compiles fine as the headers are present, but you'll get linker errors stating that uafxcw.lib, uafxcwd.lib and similar files are not found. Only ATL is present for ARM.
The first thought was to use dll2lib to generate import library from a mfc110.dll, but that DLL exports functions only by ordinals. So I've decided to look for mfc*.def in the "Program Files (x86)\Microsoft Visual Studio 11.0\VC\atlmfc\src" dir and was surprised not to find it there. And there was also no makefile, unlike older MFC versions.
I've decided to compile a static MFC library. In this case linker would ignore all extra/unnecessary files I could add into the project by mistake. Created a new Win32 project, selected "Use MFC as a static library", added the whole "src\mfc" directory except for dll*.cpp and localized resource files - and the project compiled successfully! But after adding the generated MFC.lib to a project - got errors complaining on the AfxRegisterMFCCtrlClasses. 
Googling on that function gave a link to a Microsoft blog describing its purpose. But there was no such function in whole MFC sources! After looking into the x86 uafxcwd.lib I've got that it is implemented in the afxctrlcontainer2.obj, so the file is probably named afxctrlcontainer2.cpp. Obviously I have not found that file. Several other functions were not present too:
CMFCControlContainer::UTF8ToString_AfxDispatchCall (it is implemented in x86/x64 ASM, and I was too lazy to rewrite it in THUMB), seems that they deal with MFC and OLE controls embedded into dialog resources. It would not be difficult to reimplement them by looking in disasm or implementation in older MFC versions, but I'm too lazy to do that myself.
I don't need OLE or MFC controls in my dialogs - so I've defined _AFX_NO_MFC_CONTROLS_IN_DIALOGS in stdafx.h of my project, and implement those functions as stubs.

Here is the code of all stubs:

    void __cdecl AfxRegisterMFCCtrlClasses(void)
    extern "C" int __afxNoMFCControlContainerInDialogs=1;
    extern "C" DWORD_PTR AFXAPI
    _AfxDispatchCall(AFX_PMSG pfn, void* pArgs, UINT nSizeArgs)
       return 0;
    int __cdecl CMFCControlContainer::UTF8ToString(char const *,class 
        ATL::CStringT<wchar_t,class StrTraitMFC<wchar_t,
        class ATL::ChTraitsCRT<wchar_t> > > &,int)
       return 0;

After adding that code to MFC lib sources - my project compiled and works correctly on Windows RT.
Debug MFC library is 157 Mb in size, and release version is 1.2 Gb. This is much more than the same x86 LIBs. Probably this is due to embedded debugger and incremental linker information. 

The compiled LIB files are published in this post on XDA. Do not forget to add 
to your stdafx.h and avoid using custom MFC controls in dialog designer.

Added on 28 april:
There is an implementation of UTF8ToString and AfxRegisterMFCCtrlClasses functions here:
And here I've found the THUMB implementation of _AfxDispatchCall function:
It is probably taken from Windows CE MFC library.
I've recompiled MFC libs with that code, uploaded sources and libs here:

Added on 7 may:
danesh110 on XDA found that beta version of Visual Studio 2012 (it was called Visual Studio 11) contained the MFC libraries and DLLs for Windows RT. More info here: http://forum.xda-developers.com/showthread.php?p=41133829#post41133829
VS 11 beta can be downloaded from the links in this blog: http://www.kunal-chowdhury.com/2012/03/download-visualstudio11-beta.html
Beta has more sources, for example the managed mfc and ARM assembler version of _AfxDispatchCall function, but they are still incomplete.

No ARM code on Windows on ARM

He-he. No ARM code on Windows on ARM. Period.
Though MS called their new OS "Windows on ARM" (sometimes Windows on Snapdragon, Windows on Tegra and now Windows RT) - you can't see ARM code in it. Everything is THUMB2.
I don't know the reason of such a limitation - but that caused me several days of debugging while I was working on a DosBox dynamic core WinRT port last year. The dynamic code generated by DosBox is ARM, even the original THUMB generator was using small ARM thunks. Original DosBox works fine on ARM Linux, but on Windows RT it behaves differently. The generated code executes once, twice, but on the 100500 time - it crashes.
The reason was simple. If you read the ARM developer's manual, you'll see that OS should determine the previous mode (ARM or THUMB) on kernel mode entrance (via SWI, interrupt, ...). Seems that MS decided to simplify the handler's code - and assume that the user's code is always THUMB. And they always set the T bit on return to the user land.
So if you are running generated ARM code in your program - you may be forced to the THUMB mode any moment by an interrupt or a task switch.

Another ARM-code error I've came across.
In my project I need to emulate dialog procedures of the x86 program. Windows calls my own DlgProc, where I pass its parameters to the original x86 function through the emulation layer. But where to store the x86 dialog function address to call it from my code? You can't use SetProp() as the window is not created yet. CreateDialogXxxParam can't be used too - parameter is sent in WM_INITDIALOG message, while it is not the first message you receive (WM_SETFONT and other messages precede it).
So I've decided to generate a small thunk that calls my DlgProc prepending the x86 address before a first parameter. Generated THUMB2 code is simple, tested it in a separate project - idea works fine. But in main project it works once, twice, but on 10th time - the program crashes. And in the exception you see that the lowest bit of LR register points to your code, but is zero. This means that the previous code was executed as ARM, not THUMB2 as I've expected! It took me 2 days to debug. The reason was simple - I've forgot to set the bit 0 of the generated thunk address to one while passing it as DlgProc to the Windows API to indicate that it points to THUMB code. For normal functions Visual Studio does this automatically - but for generated code you should do that yourself.
Windows kernel was "kind enough" to fix that for me 9 times, but on 10th - something went different and my thunk was executed as ARM code. Setting the lowest bit of an address to one - fixed the problem immediately.

During debugging I've found a good article on the internals of Windows kernel to user mode callbacks here. Though it is about a desktop - it is worth reading anyway.