views:

1603

answers:

9

I have an application that needs to operate 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?

+2  A: 

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.

Steve314
#1. How does somebody do that? Also, this must be a standalone EXE. I'm statically linking to the library and cannot supply any DLLs to the system. #2. Again, I'm unaware how to do that. #3+4. Ok. #5. We're back to the "I can't include DLLs" problem :( +1 for taking the time to write a good answer though.
Billy ONeal
For #1, I'm talking about the lib file that gets linked into your app. It's just a file - it can be modified. You should already have lib.exe for doing things like extracting obj files from lib files. For the next step, you could look up obj2asm, an obj file disassembler.
Steve314
Symbol lookup in general happens in listed order. If you provide "EncodePointer.lib" after the CRT but before KERNEL32.LIB, the linker will use that. However, you need the option "no default libs" to be able to list KERNEL32 explicitly last.
MSalters
@MSalters: The problem is generating such a lib -- if you try to implement EncodePointer or DecodePointer the compiler complains about not being able to implement a function marked as DLLIMPORT.
Billy ONeal
@Billy ONeal: When implementing them (in a .LIB), just don't declare them as DLLIMPORT. In that context, they're not.
MSalters
@MSalters: Err.. they're already declared DLLIMPORT in the windows headers.
Billy ONeal
Your library is supposed to _replace_ the Windows implemenations of EncodePointer and DecodePointer, therefore it shouldn't include Windows headers either.
MSalters
+4  A: 

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).

Ben Voigt
Couple problems here. #1: That dll would still have dependency issues if I'm not mistaken, #2: a requirement for this application is that it makes no writes to the filesystem.
Billy ONeal
The DLL would have platform dependencies, but you would not load it until you had verified the platform you were running on.Ben Voigt's supplementary point about how to package your application is secondary to your question, and there is no need to perform any writes to the file system.
Seb Rose
+2  A: 

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.

Gideon7
I tried to do this, but the compiler complains because the CRT has the offending functions marked as DLLIMPORT, and you cannot implement DLLIMPORT functions in a static library. You would have to recompile the CRT.
Billy ONeal
Why not implement these functions in a DLL?
Seb Rose
Implement these functions in a DLL and link against it as described by Gideon7 (when you build a DLL a .LIB file is also generated). These functions will need to be exported from the DLL as documented on the MSDN - http://msdn.microsoft.com/en-us/library/3y1sfaz2.aspx
Seb Rose
@Seb Rose: Because for this application I cannot have anything other than the one EXE. Single binary XCOPY deployment is very important for this application. It **must** be statically linked.
Billy ONeal
@Billy ONeal: So how much functionality do you need from the standard library? Could you get away without it, loading Windows DLLS explicitly and calling required functionality directly?
Seb Rose
@Seb: Unfortunately not :( Looks like I'm screwed into using VS2008 :(
Billy ONeal
+3  A: 

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.

Head Geek
Visual Studio 9.0 is Visual Studio 2008. That doesn't help here -- VS2008's toolset can already build for Win2k. RE: `(There should already be a #define for that.) ` <-- There is not.
Billy ONeal
I've never been able to keep track of which real version number goes with which year. Visual Studio 10.0 should work the same; I don't have a copy installed on any of my machines at present, or I'd confirm it for you. If there's no #define for using those functions, you could easily create a pair of functions of your own to replace them, calling the real ones on systems that support them and just returning the passed-in values on all others (or just using the pass-through version on all systems, if it works). Some preprocessor macro magic should take care of any differences.
Head Geek
I agree. VS2010 comes with the CRT source code; install it, copy it somewhere, remove the references to EncodePointer/DecodePointer, edit the BLDNT.CMD so that you get a static lib, and off you go.
Eric Brown
+14  A: 

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 typedefs 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.

AshleysBrain
I concur. The other solutions presented are "cool" and that's the problem. I wouldn't dare ship a product with such "cool" hacks. What if things doesn't work out? It will be hell to debug and trace. I love auto, lambdas, r-value reference and decltype as much as the next guy but not at any expense.
FuleSnabel
+9  A: 
Suma
+1, neat and *relatively* clean solution. Darn annoying that MS has been adding (imho unnecessary) "kill off older Windows" support stuff since vs2005.
snemarch
Ah bother, this of course won't work - __imp_* are dword pointers to functions. The generated code is effectively interpreting the machine code of your De/EncodePointer functions as pointers and trying to call that memory location. You need to generate __imp_* pointers pointing to the dummy functions.
snemarch
A: 

I am having the same problem, and I tried the answer provided by Suma above. I have no trouble getting the linker to find my overridden EncodePointer/DecodePointer import vectors, but the program consistently crashes at runtime. (Segfault -> bad instruction pointer). I've stepped through the disassembly and it all looks solid to me -- I have no idea what is going on.

Compile this file:

extern "C" {

void * __stdcall imp_DecodePointer(void *x) {return x;} void * __stdcall imp_EncodePointer(void *x) {return x;}

};

int main( void ) { return 0; }

with MSVC 2010: cl.exe /c main.cpp link.exe main.obj

I've tried every permutation of /NODEFAULTLIB for kernel32.lib and libcmt.lib. The program crashes consistently on a call to EncodePointer made by the CRT initialization functions. Stack trace:

0034ff30 00b92075 00000000 00b912e8 76f51484 0x31e9

0034ff38 00b912e8 76f51484 00b924e8 00000000 main!_encoded_null+0x8 [f:\dd\vctools\crt_bld\self_x86\crt\src\tidtable.c @ 81]

0034ff40 00b924e8 00000000 00000000 00b91150 main!_init_pointers+0x8 [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0dat.c @ 810]

0034ff4c 00b91150 a4cfec4b 00000000 00000000 main!_mtinit+0xcb [f:\dd\vctools\crt_bld\self_x86\crt\src\tidtable.c @ 256]

0034ff88 76f53677 7efde000 0034ffd4 77479d72 main!__tmainCRTStartup+0x80 [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 228]

0034ff94 77479d72 7efde000 5708d4b0 00000000 kernel32!BaseThreadInitThunk+0x12

0034ffd4 77479d45 00b91231 7efde000 00000000 ntdll32!RtlInitializeExceptionChain+0x63

0034ffec 00000000 00b91231 7efde000 00000000 ntdll32!RtlInitializeExceptionChain+0x36

Suma, do you know the reason for this? Thanks!

DSII
snemarch's solution looks like the answer. Thanks!
DSII
too bad I couldn't figure out a solution without turning to using assembly, but it's IMHO the easiest fix :)
snemarch
+4  A: 

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
snemarch
+1  A: 

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.

Tomasz Grobelny