【问题标题】:c# visual studio build exe with target anycpu but that determines its platform(x86/x64) on the calling process platform(x86/x64)c# visual studio build exe 与目标anycpu 但这决定了它在调用进程平台(x86/x64)上的平台(x86/x64)
【发布时间】:2017-06-11 20:20:09
【问题描述】:

我们有一个 32 位和 64 位的软件,它调用我们的 exe 并将事件传递给它(就像一个插件)。

问题是我们的 exe 必须以与调用软件相同的位数(x86/x64)执行(如果软件以 32 位版本运行,我们的 exe 必须以 32 位运行,如果软件以 64 位运行版本我们的 exe 必须以 64 位运行)。 windows版本为64位,但客户端可以运行32位或64位版本的软件。

在 Visual Studio(2015) 中,Target AnyCPU 选项仅取决于 windows 版本(+ 首选 32 位复选框),但我们需要依赖于调用软件进程。

有没有我们可以实现的选项或解决方案,而不是编译到每个平台(x86 和 x64)?

【问题讨论】:

  • “调用我们的 exe”没有什么意义。插件总是作为 DLL 加载,该文件具有 .exe 文件扩展名在 .NET 中无关紧要。任何 DLL 都需要符合启动可执行文件选择的位数。插件场景中的宿主进程。通过选择“AnyCPU”作为目标并选中“首选 32 位”复选框,在 C# 中很容易完成。你真的试过吗?
  • 对不起,如果我解释错了。在这种情况下,软件显式运行ourapp.exe(是它自己的进程而不是插件),因此有两个独立的进程:software.exe 和ourapp.exe基于它正在运行的窗口的位数(逻辑上如文档所示),但我们想要一些选项,将其位数基于调用我们的 exe 的进程(引导样式?)
  • 您对software.exe有任何控制权吗?能否为ourapp.exe创建一个32bitWrapper和一个64BitWrapper,让software.exe任意启动?
  • 根据我的经验,“首选 32 位”实际上意味着“强制 32 位”。我从来没有喜欢在 64 位模式下运行的 32 位应用程序。也许是这样,但根据我的经验不是。

标签: c# visual-studio-2015 x86 target-platform anycpu


【解决方案1】:

Windows 操作系统没有要求两个单独的进程必须具有相同的位数才能相互通信——所以我假设您有一些内部要求,即两个进程必须具有相同的位数。如果您绝对必须在 32 位或 64 位中动态运行相同的 EXE 并在运行时做出决定,您可以在运行时使用 Visual Studio 附带的 corflags.exe 实用程序修改您的 AnyCPU 编译应用程序。

您可以像这样启动 corflags.exe(使用 System.Diagnostic.Process):

corflags.exe "ourapp.exe" /32BIT+

强制它运行32位,并且

corflags.exe "ourapp.exe" /32BIT-

回到 AnyCPU(在 64 位操作系统上将是 64 位)

解决方案是在运行时环境中部署 corflags.exe 的副本。然后,您的主机应用程序可以通过检查 sizeof(IntPtr) 来检测它的当前位数以查看它是 8 还是 4;并在启动 ourapp.exe 之前相应地生成 corflags.exe 的副本(使用 System.Diagnostics.Process)。

这非常HACKY。当心。显然,如果您需要在同一台机器上同时运行两个 ourapp.exe 副本,您将遇到很多麻烦。理想的做法是在修改之前将 ourapp.exe 的唯一副本创建到本地用户文件夹并从那里启动它,以避免来自多个实例的竞争条件和 UAC 提示,具体取决于您的目标环境。

【讨论】:

    【解决方案2】:

    进程必须在任务管理器的进程选项卡中显示为自身是否是绝对要求?或者它可以在另一个主机进程中运行吗?

    如果是后者,您可以创建一个包含以下 Main 方法的存根项目,然后编译两个版本的可执行文件:一个为 32 位 (LaunchX86.exe),另一个为 64 -位(LaunchX64.exe)。

    static int Main(string[] args)
    {
        try
        {
            foreach (var t in Assembly.LoadFile(args[0]).GetTypes())
            {
                var methodInfo = t.GetMethod(
                    "Main",
                    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
    
                if (methodInfo != null)
                {
                    int? retVal = 0;
    
                    // See http://stackoverflow.com/questions/2378016/how-to-run-something-in-the-sta-thread#2378077
                    var thread = new Thread(() =>
                    {
                        try
                        {
                            // Main() methods may have zero or one parameters
                            var methodParams = methodInfo.GetParameters().Length == 0
                                ? null
                                : new object[] {args.Skip(1).ToArray()};
    
                            retVal = methodInfo.Invoke(t, methodParams) as int?;
                        }
                        catch (Exception e)
                        {
                            retVal = 99; // Error 99: The executable itself threw an exception
                        }
                    });
    
                    thread.SetApartmentState(ApartmentState.STA);
                    thread.Start();
                    thread.Join();
    
                    return retVal ?? 0;
                }
            }
    
            return 98; // Error 98: unable to launch exe because no method "Main" found
        }
        catch (Exception e)
        {
            // Can identify exception type here and return appropriate result, e.g.:
            // ArgumentNullException - no EXE name provided in first param
            // BadImageFormatException - specified file was not a suitable EXE
    
            return 97; // Error 97: unable to load executable for analysis
        }
    }
    

    注意:为了简单起见,上面对异常的捕获非常粗略,并将它们映射到一些固定的返回值。

    另外,请注意,在 Invoke() 方法中,我们不会将任何参数传递给 WPF 应用程序的 Main() 方法,因为 WPF 应用程序会自动接收与启动器相同的参数。这并不理想,但可以使用各种解决方法,例如在启动之前使用Environment.SetEnvironmentVariable() 设置环境变量,使用Environment.GetEnvironmentVariable() 在目标应用程序中检查该变量,以及(如果存在)使用其内容代替正常方法。

    然后根据您当前进程的位数从您的主应用程序启动相关的主机可执行文件:

    new Process
    {
        StartInfo = new ProcessStartInfo(
            Environment.Is64BitProcess ? "LaunchX64.exe" : "LaunchX86.exe",
            "application_to_be_hosted.exe param1 param2 etc."
        )
    }.Start();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-10
      • 2017-08-03
      • 1970-01-01
      相关资源
      最近更新 更多