我可以在Visual Studio 2008的C ++运行时库中使用Visual Studio 2010的C ++编译器吗?

时间:2010-03-20 19:57:35

标签: c++ visual-studio visual-studio-2008

我有一个需要在Windows 2000上运行的应用程序。我还想使用Visual Studio 2010(主要是因为auto关键字定义的更改)。但是,我有点绑定,因为我需要应用程序才能在较旧的操作系统上运行,即:

  • Windows 2000
  • Windows XP RTM
  • Windows XP SP1

Visual Studio 2010的运行时库依赖于Windows XP SP2中引入的EncodePointer / DecodePointer API。

如果可以使用备用运行时库,这会破坏依赖于VS2010中添加的C ++ 0x功能的代码,例如std::regex吗?

8 个答案:

答案 0 :(得分:24)

最简单的解决方案是将VS2010中的项目设置中的Platform Toolset设置为v900,它将使用Visual Studio 2008库和编译器。这也意味着您丢失了auto等C ++ 0x功能,但说实话,使用某些typedef来解决这个问题可能比构建自己的CRT版本或其他更复杂的解决方案更容易。或者,只需使用VS2008!我不知道是否有其他C ++ 0x功能对你的应用程序至关重要,你没有提到 - 除了std::regex,我认为它仍然在技术报告1下的v900工具集中命名空间(std::tr1::regex)。

仅仅从我得到的印象中,我预测让VS2010库在XP SP1上运行的不便大于C ++ 0x功能的便利性,所以整体来说它不值得。

答案 1 :(得分:24)

您无法使用2008 CRT,但您可以阻止新功能DecodePointer / EncodePointer从内核链接。用存根替换新函数非常容易。

有人可能会尝试跟随:将这样的代码放在main.cpp源代码中:

extern "C" {

  void *__stdcall _imp__DecodePointer(void *x) {return x;}
  void *__stdcall _imp__EncodePointer(void *x) {return x;}

};

<击>

以上不起作用。虽然基本思想是合理的,但执行需要有点不同。正如snemarch在评论和another answer中所描述的那样,__imp__不能是函数调用,只能是指向它的指针。由于似乎无法直接由编译器生成指针,因此需要使用MASM汇编以下代码并链接到生成的目标文件。

.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

项目中的符号优先于库中的任何符号。 DLL库使用.lib部分链接,这些部分仅包含跳入实际函数的__imp__个“向量”。通过替换__imp__“向量”,您不会触及DLL链接,而是替换.lib部分。我已经验证了exe对DecodePointer / EncodePointer没有任何依赖性。

背景

静态链接库仅将已使用的功能带入应用程序。可以使用链接器详细进度输出找到哪些特定CRT函数带来这些新API:

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

这表明在一些CRT中使用了新的API,以便为一些被认为提供频繁攻击向量的函数提供更高的安全性。

通过一些努力,可以使用LoadLibrary / GetProcAddress来提供OS提供的真实功能,但我认为它不会真正带来任何东西。使用DecodePointer / EncodePointer的运行时函数并不真正需要它来提供任何编码,他们只需要通过对称进行编码。您并不真正需要增强的安全性(VS 2008运行时也不会提供给您)。

我希望没有其他障碍等着你 - 我无法访问Win2k或XP pre SP2系统,因此我无法尝试。如果有任何exe标头标志阻止甚至试图在这样的系统上启动exe,它们应该很容易改变。

答案 2 :(得分:11)

Suma's solution看起来非常有前途,但它不起作用:__imp__*@4符号需要指针指向函数,而不是函数本身。不幸的是,我不知道如何让Visual C ++吐出一个带有这种名称生成的指针......(好吧,__declspec(naked)结合__stdcall就可以了,但是我不知道知道如何发射指针。)

如果在构建时使用汇编程序是正常的,那么解决方案非常简单 - 使用FASM汇编以下代码并链接到生成的目标文件,并且在exe中没有EncodePointer / DecodePointer引用:

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

答案 3 :(得分:8)

由于Visual Studio附带了对MASM的支持(请参阅项目属性 - &gt;构建自定义...),以下将snemarch的代码转换为MASM可能很有用:

.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

请记住将Linker-&gt; System-&gt; Minimum Required Version设置为5.0(默认为5.1)以在Windows 2000上运行。

答案 4 :(得分:4)

此问题的常见解决方法是构建自己的CRT自定义版本。它有说明here。您只需编辑代码即可忽略EncodePointerDecodePointer。 (应该已经有#define。)

您还需要做其他两件小事:

  • 转到Linker-&gt;其他库目录设置,并将C:\Microsoft Visual Studio 9.0\VC\lib设置为第一个搜索路径。 (我假设您使用了默认安装目录,否则将根据需要进行更改。)
  • 将PE标题中的子系统版本更改为5.00(如果您没有其他工具,请使用免费的CFF Explorer Suite)。

这应该允许您的程序在Windows 2000以及更高版本上运行。

答案 5 :(得分:3)

选项1 - 创建2010运行时的修改版本,将问题API调用重定向到您提供的DLL。我不知道这会有多么简单或困难 - 希望只是对符号表进行一次小调整,但这取决于文件格式 - 当然,你很可能会遇到许可证的逆向工程条款

选项2 - 比较两个不同版本的运行时库中的导出符号。如果符号相同,则具有良好的兼容性 - 尽管没有保证。它甚至可能是lib文件格式不同。

选项3 - 检查您是否可以通过MSDN或类似方式访问运行时源,特别是为了创建修补版本。

选项4 - 检查您是否可以使用2010编译器,但是可以使用较旧的链接器,可能在您的解决方案中将其配置为自定义构建步骤。同样,这取决于obj和lib文件是否是相同的文件格式 - 但您可以编写一个小实用程序来修补标头中的版本号之类的简单差异。旧的链接器应该在旧的运行时链接没有问题 - 假设新编译器的objs与它兼容。

选项5 - 在2010年构建DLL,它们不需要自己的运行时,但是由使用旧编译器构建的应用程序加载和托管。实现DLL的“无运行时”要求可能意味着必须在托管应用程序中构建许多库,并且您可能需要提供自己的接口(通过主机应用程序)到您需要工作的库函数与 - 特别是内存分配。

值得检查的选项,但我确定你已经想到了所有这些 - 对不起我不知道它们中的任何一个是否会起作用 - 或者它们是否会起作用但会导致间歇性问题。

答案 6 :(得分:3)

如果允许使用DLL,这将更容易。基本上,通过使用链接器/ ENTRYPOINT函数编写一个完全不需要C运行时函数的EXE。一旦您测试了您的基本先决条件,并通过所有目标操作系统(即MessageBox)上提供的Windows提供的API向用户报告任何问题,然后调用LoadLibrary以启动包含大量逻辑的DLL 。该DLL可以像往常一样使用VS2010运行时。您甚至可以通过在启动时从主.EXE中包含的资源解压缩DLL来避免部署两个单独的文件。 (您可以在内存中完全执行此操作而无需将.DLL写入磁盘,但如果您想利用Windows PE加载程序来修复所有导入,则无法完成此操作。)

答案 7 :(得分:0)

创建一个实现缺失功能的.LIB,并将其链接到KERNEL32.LIB之前。

您需要使用链接器选项/NODEFAULTLIB:kernel32.lib,以便将w2kcompat.lib置于kernel32.lib之前。