8

我们有一个 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)?

4

2 回答 2

4

没有 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 提示,具体取决于您的目标环境。

于 2017-02-03T18:02:33.237 回答
0

Is it an absolute requirement that the process must appear as itself in the Processes tab of Task Manager? Or can it run contained inside another host process?

If the latter, you could create a stub project containing the following Main method and then compile two versions of the executable: one as 32-bit (LaunchX86.exe) and the other 64-bit (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
    }
}

Note: to keep things simple, above has very coarse catching of exceptions and maps those to a few fixed return values.

Also, note that in the Invoke() method, we are not passing any parameters to the Main() method for WPF applications, because the WPF application automatically receives the same parameters as those of the launcher. This is not ideal but various workarounds are possible, such as setting an environment variable using Environment.SetEnvironmentVariable() before launching, checking for that variable within the target application using Environment.GetEnvironmentVariable() and (if present) using its contents in place of the normal method.

Then from your primary application you launch the relevant host executable depending on your current process's bitness:

new Process
{
    StartInfo = new ProcessStartInfo(
        Environment.Is64BitProcess ? "LaunchX64.exe" : "LaunchX86.exe",
        "application_to_be_hosted.exe param1 param2 etc."
    )
}.Start();
于 2017-02-08T23:42:21.193 回答