C++/C#解决方案中无法使用任意CPU

18
我有一个包含C#和托管C++项目的解决方案。该解决方案可以在x64和x86平台下编译。由于使用了托管C++,我想创建一个“任何CPU”解决方案,并摆脱旧解决方案。
我将C++项目链接器设置更改为对x64和x86都强制启用安全IL镜像。
接下来,使用“配置管理器”,我创建了一个名为“Any CPU”的新解决方案平台。然后我也添加了一个名为“Any CPU”的项目平台。
我继续将所有C#项目设置为'Any CPU',但是对于C++项目,我无法这样做。项目平台“Any CPU”不在下拉菜单中,并且也没有选项'New...'。
Visual Studio对此非常坚定,因此我将其保持不变并开始构建。令我惊讶的是,生成的DLL(来自C++项目)是MSIL,即使C++的平台是x64。编译x32时,结果DLL也是MSIL。
为什么? 为什么我不能将C++项目设置为“任何CPU”?

2
可能是Managed C++和AnyCPU的重复问题。 - Christopher Painter
1
我认为不是这样的,因为C++项目生成的DLL是MSIL格式。因此,没有必要使用加载x64或x86的“奇怪”操作。我只是不明白为什么我无法将托管C++编译为“任何CPU”(在配置中)。 - Frank Kaaijk
1
试图解释特定平台名称在构建可在任何平台上运行的代码时是完全无关紧要的,这是一项永远不会完成的艰巨工作。唯一重要的是EXE项目中的Jitter覆盖。 - Hans Passant
1
@ChristopherPainter:这不是那篇帖子的重复。你提出的那篇帖子正在讨论使用特定于平台的C++/CLI DLL,根据平台自动选择。在这里,OP想要一个C++/CLI DLL程序集,具有与C#“AnyCPU”程序集相同的特性。也就是说,一个可以在任何平台上运行的单一程序集。 - Peter Duniho
2个回答

17
据我所知,在Visual Studio中,您无法为C++/CLI项目创建"AnyCPU"项目类型。但是,您可以配置您的C++/CLI项目(在"Win32"项目类型下)以纯净、安全的MSIL方式进行编译,而不需要目标平台。这样做将允许您的C++/CLI DLL程序集与"AnyCPU" C#项目一起使用。也就是说,它实际上是“AnyCPU”,尽管这不是它在配置管理器中的实际名称。
在"C/C++"项目设置中: - 通用语言运行时支持: 安全 MSIL 通用语言运行时支持 (/clr:safe) 在"链接器"项目设置中: - CLR映像类型:只需确保此项未明确设置为IJWPURE 注意: - 使用"safe"项目类型,会忽略掉一些似乎影响平台类型的编译器和链接器选项。即,您不必去设置所有内容为非特定平台类型,只需按照上述步骤。但是如果您觉得有必要,您可以将其他选项设置为适当的值。 - "Safe"将防止使用指针。如果这很重要,则显然可以通过更复杂的过程来解决。详情请参见从C++/CLI项目创建纯MSIL程序集? - 切勿忘记,默认情况下,Visual Studio会创建C#项目,即使它们是"AnyCPU",即使它们在64位操作系统上执行,也将作为32位进程启动。如果依赖项与预期的纯/安全MSIL不同,则可能隐藏平台不匹配问题,这是需要注意的(您可以通过取消选中C#项目的“生成”项目属性页面中的"偏好32位"选项来控制此行为)。

这个答案与我在解决方案中所看到的情况是一致的。我无法找到任何关于MSDN的参考资料来解释Peter所写的内容,即“您无法在Visual Studio中为C++/CLI项目创建“AnyCPU”项目类型”。 - Frank Kaaijk
在VS2015 Update 5中,这会生成一个编译器警告D9035,告诉我们我们正在编译已过时且将在未来版本中取消支持的Managed C++(尚未尝试2017,也许这个答案已经不再有效)。很遗憾,因为这正是我们想要的,一个AnyCPU编译器/链接器配置文件。 - Tony Wall
这不是一个有效的答案,因为如果你只生成MSIL代码,那么你本来就不应该用C++编写代码。如果你只使用.NET Framework和MSIL库编写软件,那么试图用C++编写只会让事情变得更加困难。我唯一能想象到的好处可能是在PInvoke代码方面,但警告和非标准/废弃的方法让我对这个解决方案失去了兴趣。 - Tony Wall

7
为了让C++功能能够被C# dll所使用,C++项目必须生成x86和x64版本的dll。无法仅从使用AnyCPU设置编译的C# dll中引用x86或x64 dll。
解决方法是在运行时确保程序集无法加载C++ dll,然后订阅AppDomain AssemblyResolve事件。当程序集尝试加载dll并失败时,您的代码有机会确定需要加载哪个dll。
订阅该事件的方式如下:
System.AppDomain.CurrentDomain.AssemblyResolve += Resolver;

事件处理程序长这样:

System.Reflection.Assembly Resolver(object sender, System.ResolveEventArgs args)
{
     string assembly_dll = new AssemblyName(args.Name).Name + ".dll";
     string assembly_directory = "Parent directory of the C++ dlls";

     Assembly assembly = null;
     if(Environment.Is64BitProcess)
     {
            assembly = Assembly.LoadFile(assembly_directory + @"\x64\" + assembly_dll);
     }
     else
     {
            assembly = Assembly.LoadFile(assembly_directory + @"\x86\" + assembly_dll);
     }
     return assembly;
}

我创建了一个简单的项目,演示了如何从一个AnyCPU dll中访问C++功能。 https://github.com/kevin-marshall/Managed.AnyCPU

1
谢谢Toby,我已经更新了我的答案,希望它更具信息性。 - Kevin Marshall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接