Visual C++ 2010 MFC applications to run on Win2k

Windows 2000 fixes and solutions for apps

Visual C++ 2010 MFC applications to run on Win2k

PostPosted by OldBoy2k » Sat Mar 31, 2012 4:25 pm

Ted's Blog: Pushing the limits of Visual C++ compatibility
Thanks Ted for posting this.

One of the annoying things about Visual Studio 2010 is that it does not create applications that can run on Windows 2000, Windows XP up to SP1, and Windows Server 2003 RTM. This can be embarrassing to say the least, you ship a cool app, and someone on one of these platforms gets an annoying error. See
http://qualapps.blogspot.com/2010/04/vi ... ndows.html
for more information about this limitation.

Recently, someone over at stackoverflow discovered a quick and easy way of running Win32 (non-MFC) apps on Windows 2000 through the use of a little bit of assembly code – see:
http://stackoverflow.com/questions/2484 ... -c-runtime
Ignore the title of the above stack overflow question for the moment – the important part for our purposes, is Tomasz Grobelny’s MASM translation of snemarch’s FASM answer. I say this because Visual Studio 2010 has MASM tools built-in so we don’t need any extra downloads.

Let me explain. Win32 apps compiled with Visual C++ 2010 have a couple of hard dependencies (EncodePointer/DecodePointer) that we want to get rid of. The bit of assembly in the above article is exactly what we need.

I won’t go into a lot of details about that here, so if you’re only interested in Win32 apps the following article may not be of interest to you, except for the part about configuration of MASM in your project (keep reading below).

So what about MFC apps? Reading Jim’s article above, as you can see there are 4 more dependencies we want to get rid of, all Activation context functions.

So, how can we easily get rid of these without rebuilding MFC?

I had hoped it would be as easy as replacing afxstate.cpp with Visual Studio 2008 SP1′s version (which has most of the wrapper of the activation context APIs), however, Microsoft made it a bit more difficult for us. Looking at the source code and includes for mfc (if you have WinDiff handy and both 2008 SP1 and 2010 installed, feel free to following along with me) – in afxcomctl32.h – notice the devastating change in the macros: AFX_ISOLATIONAWARE_COMMON_ACTIVATE and AFX_ISOLATIONAWARE_FUNC_DEACTIVATE: Microsoft has changed the calls of AfxActivateActCtxWrapper to ActivateActCtx, and similarly AfxDeactivateActCtx to DeactivateActCtx. Unfortunately, these macros are baked into the MFC object code provided by Microsoft. Nothing we do is going to change that. So we have to workaround this problem. More on that later.

So, how about we replace afxstate.cpp as a start, with the 2008 implementation. Another way to accomplish the same thing, is to include a few redefining macros in our code, then immediately after, include the 2010 code in our stdafx.cpp. This will replace the obj that we’re linking to with the one in our project.

Now, back to those pesky calls to ActivateActCtx and DeactivateActCtx in the include file. This is where the first article at stack overflow helped me immensely. But, instead of doing what they did with EncodePointer and DecodePointer (created dummy functions instead of actually doing the encoding/decoding), we’re going to redirect any call ActivateActCtx and DeactivateActCtx in our binary over to our own Afx versions.

I won’t go into details about the assembly code here, suffice it to say, I’m not a very good assembly programmer, so this part was the most time consuming part of my research. Once I discovered the invoke command, it became easier to create a solution.

The final step of the puzzle was realizing that MFC apps have a hard dependency on gdiplus.dll, so on Windows 2000, make sure you copy gdiplus.dll to your program folder before running (there are exceptions to this requirement for certain apps, more on that below)

So, after all that, here it is, a walkthrough on how to create an MFC app in Visual Studio 2010 that runs on Windows 2000 (resulting executable tested on Windows 2000 SP4 including Update Rollup 1 KB891861 which is required for the HeapSetInformation function)

1) Launch Visual Studio 2010
2) Create an MFC app, keep all the defaults (yes, leave all that feature pack stuff on too :)) Hit Finish.
3) Right click on the project and hit Properties. Choose from configuation dropdown: All Configurations
4) Under configuration properties – General, choose Use of MFC – Use MFC in a Static Library
5) Under Linker – System, type in 5.0 beside Minimum Required version
6) Under Linker – Input, type in gdiplus.dll beside Delay loaded Dlls and then hit OK.
7) Right click on the project and choose “Build Customizations…” menu item.
8 ) Click on the checkbox beside “MASM (.targets,.props), and hit OK.
9) Right click on Source Files folder, and choose Add – New Item
10) Click on Code, then C++ file, then type in pointer.asm (not pointer.cpp) in the Name field and hit OK.
11) In the newly created pointer.asm, paste in the following MASM assembly code:

.model flat, C

AfxActivateActCtxWrapper PROTO STDCALL :DWORD,:DWORD
AfxDeactivateActCtx PROTO STDCALL :DWORD,:DWORD

.data
__imp__EncodePointer@4 dd dummy
__imp__DecodePointer@4 dd dummy
__imp__ActivateActCtx@8 dd ActActCtx
__imp__DeactivateActCtx@8 dd DeacActCtx

EXTERNDEF __imp__EncodePointer@4 : DWORD
EXTERNDEF __imp__DecodePointer@4 : DWORD
EXTERNDEF __imp__ActivateActCtx@8 : DWORD
EXTERNDEF __imp__DeactivateActCtx@8 : DWORD

.code
dummy proc
mov eax, [esp+4]
ret 4
dummy endp

ActActCtx proc uses ebx ecx hActCtx:DWORD,lpCookie:DWORD
invoke AfxActivateActCtxWrapper, hActCtx, lpCookie
ret 8
ActActCtx endp

DeacActCtx proc uses ebx ecx dwFlags:DWORD,ulCookie:DWORD
invoke AfxDeactivateActCtx, dwFlags, ulCookie
ret 8
DeacActCtx endp

end

12) Save the pointer.asm file, and open the stdafx.cpp file
13) Paste in the following code in the stdafx.cpp file (after the #include)


#define DELETE_EXCEPTION(e) do { if(e) { e->Delete(); } } while (0)
#include "afxpriv.h"

HANDLE AFXAPI AfxCreateActCtxW(PCACTCTXW pActCtx);
void AFXAPI AfxReleaseActCtx(HANDLE hActCtx);
extern "C" BOOL AFXAPI AfxActivateActCtx(HANDLE hActCtx, ULONG_PTR *lpCookie);
extern "C" BOOL AFXAPI AfxDeactivateActCtx(DWORD dwFlags, ULONG_PTR ulCookie);
extern "C" eActCtxResult AFXAPI AfxActivateActCtxWrapper(HANDLE hActCtx, ULONG_PTR *lpCookie);

// begin stuff ripped off from VC2008 SP1 afxstate.cpp

#define AFX_ACTCTX_API_INIT_PROCPTR(hKernel,name) pfn##name = (PFN_##name) GetProcAddress(hKernel, #name)
#define AFX_ACTCTX_API_PTR_DEFINE(name, type, params) typedef type (WINAPI* PFN_##name)params; PFN_##name pfn##name = NULL;
AFX_ACTCTX_API_PTR_DEFINE(CreateActCtxW, HANDLE, (PCACTCTXW));
AFX_ACTCTX_API_PTR_DEFINE(ReleaseActCtx, void, (HANDLE));
AFX_ACTCTX_API_PTR_DEFINE(ActivateActCtx, BOOL, (HANDLE, ULONG_PTR*));
AFX_ACTCTX_API_PTR_DEFINE(DeactivateActCtx, BOOL, (DWORD, ULONG_PTR));

AFX_STATIC void AFXAPI _AfxInitContextAPI()
{
static HMODULE hKernel = NULL;
if (hKernel == NULL)
{
hKernel = GetModuleHandle(_T("KERNEL32"));
ENSURE(hKernel != NULL);
AFX_ACTCTX_API_INIT_PROCPTR(hKernel,CreateActCtxW);
AFX_ACTCTX_API_INIT_PROCPTR(hKernel,ReleaseActCtx);
AFX_ACTCTX_API_INIT_PROCPTR(hKernel,ActivateActCtx);
AFX_ACTCTX_API_INIT_PROCPTR(hKernel,DeactivateActCtx);
}
}

eActCtxResult AFXAPI AfxActivateActCtxWrapper(HANDLE hActCtx, ULONG_PTR *lpCookie)
{
ENSURE_ARG(lpCookie!=NULL);

eActCtxResult eResult=ActCtxFailed;
if (pfnActivateActCtx != 0)
{
eResult=AfxActivateActCtx(hActCtx, lpCookie) ? ActCtxSucceeded : ActCtxFailed;
} else
{
eResult=ActCtxNoFusion;
}

return eResult;
}

// end of stuff ripped off from VC2008 SP1 afxstate.cpp

// initialize Context API functions
class InitContext
{
public:
InitContext() { _AfxInitContextAPI(); }
};

InitContext context;

// magic defines to avoid calling context APIs in afxstate.cpp
#define AfxActivateActCtxWrapper AfxActivateActCtxWrapperVC10
#define ActivateActCtx(hActCtx, lpCookie) (pfnActivateActCtx != 0 ? pfnActivateActCtx(hActCtx, lpCookie) : FALSE)
#define DeactivateActCtx(dwFlags, ulCookie) (pfnDeactivateActCtx != 0 ? pfnDeactivateActCtx(dwFlags, ulCookie) : FALSE)
#define CreateActCtxW(pActCtx) (pfnCreateActCtxW != 0 ? pfnCreateActCtxW(pActCtx) : INVALID_HANDLE_VALUE)
#define ReleaseActCtx(hActCtx) if(pfnReleaseActCtx != 0) { pfnReleaseActCtx(hActCtx); }

#include "..\src\mfc\afxstate.cpp"

14) Build your application.
15) Copy GDIplus.dll to your debug and/or release folder (where your EXE was built). You can get gdiplus for Windows 2000 from here:
http://www.microsoft.com/downloads/en/d ... laylang=en
16) head over to a Windows 2000 machine (or VM) and copy your debug/release folder over there, and try running the app. If all steps above were performed correctly, you now have your first MFC 2010 app running on Windows 2000.

It was quite satisfying to see that Visual C++ 2010 application come up for the first time on Windows 2000.

Note: for Win32 (non-MFC) apps follow the same directions, except you only need to keep the code in the asm relating to EncodePointer/DecodePointer, and don’t worry about the code in the stdafx.cpp. Also you’ll need to manually change the C runtime library to the non-DLL (static) versions in the project properties (this step was done for us automatically when changing MFC from shared DLL to static)


Source: How to get Visual C++ 2010 MFC applications to run on Windows 2000

Technical Blog for Jim Beveridge:
Visual C++ 2010 Apps Don't Support Windows 2000

BR
OldBoy2k
OldBoy2k
 
Posts: 1351
Joined: Fri Feb 15, 2008 5:10 pm

Re: Visual C++ 2010 MFC applications to run on Win2k

PostPosted by BlackWingCat » Wed Apr 04, 2012 12:43 am

I know the method has a little problem for SDK Based program.
and sometimes the application crashes with calling with illegal address.
I think It may be same as MFC Applications.

Before I created the VC++2010 library file for running on Windows 2000 and Windows 9x.

http://blog.livedoor.jp/blackwingcat/ar ... 52362.html
(It is for SDK Based program.)

Visual C++ 2010 SDK applications even run on Windows 95. :)
BlackWingCat
 
Posts: 75
Joined: Sat Mar 07, 2009 7:20 am
Location: Kanagawa, Japan


Return to Backporting Applications

Who is online

Users browsing this forum: No registered users and 0 guests

cron