I have an application that needs to operat开发者_如何学Pythone on Windows 2000. I'd also like to use Visual Studio 2010 (mainly because of the change in the definition of the auto
keyword). However, I'm in a bit of a bind because I need the app to be able to operate on older OS's, namely:
- Windows 2000
- Windows XP RTM
- Windows XP SP1
Visual Studio 2010's runtime library depends on the EncodePointer / DecodePointer
API which was introduced in Windows XP SP2.
If using the alternate runtime library is possible, will this break code that relies on C++0x features added in VS2010, like std::regex
?
The very simplest solution is just to set the Platform Toolset in project settings in VS2010 to v900, which will use the Visual Studio 2008 libraries and compiler. This also means you lose C++0x features like auto
, but to be honest, working around that with some typedef
s is probably easier than building your own version of the CRT or other more complicated solutions. Alternatively, just use VS2008! I don't know if there are other C++0x features that are critical to your application though, you didn't mention - other than std::regex
, which I think is still in the v900 toolset under the technical report 1 namespace (std::tr1::regex
).
Just from the impression I get, I would predict the inconvenience of getting the VS2010 libraries to run on XP SP1 is greater than the convenience of C++0x features, so overall it wouldn't be worth it.
You cannot use 2008 CRT, but you can prevent the new functions DecodePointer/EncodePointer to be linked from the kernel. It is quite easy to replace the new functions with stubs.
One might attempt following: Place code like this in your main.cpp source:
extern "C" {
void *__stdcall _imp__DecodePointer(void *x) {return x;}
void *__stdcall _imp__EncodePointer(void *x) {return x;}
};
The above does not work. While the basic idea is sound, the execution needs to be a little bit different. As described by snemarch in comment and another answer, __imp__
cannot be the function call, only the pointer to it. As it seems not possible to generate the pointer directly by the compiler, you need to assemble the following code with MASM and link against the produced object file.
.model flat
.data
__imp__EncodePointer@4 dd dummy
__imp__DecodePointer@4 dd dummy
EXTERNDEF __imp__EncodePointer@4 : DWORD
EXTERNDEF __imp__DecodePointer@4 : DWORD
.code
dummy proc
mov eax, [esp+4]
ret 4
dummy endp
end
The symbols from a project have preference against any symbols from libraries. DLL libraries are linked using .lib parts, which contain only __imp__
"vectors" jumping into the real functions. By replacing __imp__
"vectors" you do not touch the DLL linking, you replace the .lib part. I have verified there is no more any dependency of the exe on DecodePointer / EncodePointer.
Background
Statically linked library brings only used functionality into the application. It is possible to find which particular CRT function bring in those new API using linker verbose progress output:
Found __imp__EncodePointer@4
Referenced in LIBCMT.lib(crtmboxw.obj)
Referenced in LIBCMT.lib(invarg.obj)
Referenced in LIBCMT.lib(handler.obj)
Referenced in LIBCMT.lib(onexit.obj)
Referenced in LIBCMT.lib(cmiscdat.obj)
Referenced in LIBCMT.lib(tidtable.obj)
Referenced in LIBCMT.lib(hooks.obj)
Referenced in LIBCMT.lib(winsig.obj)
Referenced in LIBCMT.lib(rand_s.obj)
Found __imp__DecodePointer@4
// ... same list, only order differs ...
This shows the new APIs are used in some of the CRT to provide more security for some functions which are deemed to provide frequent attack vectors.
With some effort it would be possible to use LoadLibrary / GetProcAddress to provide the real functionality where OS offers is, but I do not think it would really bring anything. The runtime functions which use DecodePointer / EncodePointer do not really need it to provide any encoding, all they need is the encoding to by symmetric. You do not really need the enhanced security (VS 2008 runtime would not give it to you either).
I hope there are no other obstacles waiting for you - I do not have an access to Win2k or XP pre SP2 system, therefore I am unable to try. If there are any exe header flags preventing even attempting to launch the exe on such systems, they should be easy to change.
Suma's solution looked pretty promising, but it doesn't work: the __imp__*@4
symbols need to be pointers to functions, rather than the functions themselves. Unfortunately, I don't know how to make Visual C++ spit out a pointer with that kind of name generation... (well, __declspec(naked)
combined with __stdcall
does the trick, but then I don't know how to emit a pointer).
If using an assembler at build-time is OK, the solution is pretty trivial - assemble the following code with FASM and link against the produced object file, and presto - no EncodePointer/DecodePointer references in the exe:
use32
format ms coff
section ".data" data
public __imp__DecodePointer@4
__imp__DecodePointer@4 dd dummy
public __imp__EncodePointer@4
__imp__EncodePointer@4 dd dummy
section ".text" code
dummy:
mov eax, [esp+4]
retn 4
As Visual Studio comes with support for MASM (see project properties -> Build Customizations...) the following translation of snemarch's code to MASM might be useful:
.model flat
.data
__imp__EncodePointer@4 dd dummy
__imp__DecodePointer@4 dd dummy
EXTERNDEF __imp__EncodePointer@4 : DWORD
EXTERNDEF __imp__DecodePointer@4 : DWORD
.code
dummy proc
mov eax, [esp+4]
ret 4
dummy endp
end
And remember to set Linker->System->Minimum Required Version to 5.0 (default is 5.1) to run on Windows 2000.
The usual work-around for this problem is to build your own custom version of the CRT. There are instructions for it here. You'll just need to edit the code to ignore EncodePointer
and DecodePointer
. (There should already be a #define
for that.)
There are two other minor things you'll need to do:
- Go to the Linker->Additional Library Directories setting and set
C:\Microsoft Visual Studio 9.0\VC\lib
as the first path to search. (I'm assuming you used the default install directory, otherwise change as appropriate.) - Change the subsystem version, in the PE header, to 5.00 (use the free CFF Explorer Suite if you don't have another tool handy for it).
That should allow your program to run on Windows 2000 as well as later versions.
Option 1 - create a modified version of the 2010 runtime that redirects the problem API calls to a DLL that you supply. I don't know how easy or hard this would be - hopefully just a minor tweak to the symbol table, but it depends on the file format - and you're very likely to run into the reverse engineering clause of the license, of course.
Option 2 - Compare the exported symbols in two different versions of the runtime libs. If the symbols are the same, you have good odds of compatibility - though no guarantees. Its even possible that the lib file formats are different.
Option 3 - Check whether you can get access to runtime sources through the MSDN or similar, specifically in order to create a patched version.
Option 4 - Check whether you can use the 2010 compiler, but an older linker, perhaps configured in your solutions as a custom build step. Again, this depends on whether the obj and lib files are the same file format - but you may be able to write a small utility to patch simple differences like version numbers in a header. The older linker should have no problem linking in the older runtime - assuming the objs from the new compiler are compatible with it.
Option 5 - build DLLs in 2010 that don't need their own runtime, but which are loaded and hosted by an application built using the older compiler. Achieving the "no runtime" requirement for your DLLs may mean a lot of your libraries must be built in the hosting application, of course, and you may need to provide your own interfaces (via the host application) to library functions you need to work with - especially memory allocation stuff.
Options worth checking, but I'm sure you already thought of them all - sorry I have no idea whether any of them will work - or whether they'll almost work but cause intermittent problems.
This would be a lot easier if you're allowed to use a DLL. Basically, write an EXE that requires no C runtime functions at all, by using the linker /ENTRYPOINT function. Once you've tested that your basic prerequisites are met, and reported any problems to the user via only Windows-provided APIs available on all target OSes (i.e. MessageBox), then call LoadLibrary to kick off the DLL that contains the bulk of your logic. That DLL can use the VS2010 runtime as usual. You can even avoid deploying two separate files by decompressing the DLL from a resource contained within your main .EXE at startup. (You can do this entirely within memory without writing the .DLL to disk, but not if you want to take advantage of the Windows PE loader to fixup all your imports).
Create a .LIB that implements the missing functionality and link it ahead of KERNEL32.LIB.
You will need to use the linker option /NODEFAULTLIB:kernel32.lib so that you can put your w2kcompat.lib ahead of kernel32.lib.
精彩评论