C# Visual Studio сборка exe с целевым anycpu, но это определяет его платформу (x86/x64) на платформе вызывающего процесса (x86/x64)

У нас есть программное обеспечение в 32 и 64 битах, которое вызывает наш exe-файл и передает ему события (например, плагин).

Дело в том, что наш исполняемый файл должен исполняться с той же разрядностью (x86/x64), что и вызывающее программное обеспечение (если программное обеспечение работает в 32-битной версии, наш исполняемый файл должен работать в 32-битной версии, если программное обеспечение работает в 64-битной версии, наш исполняемый файл). должен работать в 64 битах). Версия для Windows является 64-битной, но клиент может запустить программное обеспечение в 32-битной или 64-битной версии.

В Visual Studio(2015) опция Target AnyCPU зависит только от версии Windows (+ Prefer 32-bit checkbox), но мы должны зависеть от вызывающего программного обеспечения Process.

Есть ли какой-либо вариант или решение, которое мы можем реализовать вместо компиляции для каждой платформы (x86 и x64)?

2 ответа

Операционная система Windows не требует, чтобы два отдельных процесса имели одинаковую разрядность для связи друг с другом, поэтому я предполагаю, что у вас есть внутреннее требование, чтобы оба процесса имели одинаковую разрядность. Если вам абсолютно необходимо добиться динамического запуска одного и того же EXE-файла в 32-разрядном или 64-разрядном режиме и принять это решение во время выполнения, вы можете изменить скомпилированное приложение AnyCPU во время выполнения с помощью утилиты corflags.exe, поставляемой с Visual Studio.

Вы можете запустить corflags.exe следующим образом (используя System.Diagnostic.Process):

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

Чтобы заставить его работать 32bit, и

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

Чтобы вернуться к AnyCPU (который будет 64-битным в 64-битной ОС)

Решение состоит в том, что вы развернете копию corflags.exe в вашей среде выполнения. Затем ваше хост-приложение может обнаружить его текущую разрядность, проверив sizeof(IntPtr), чтобы увидеть, равно ли оно 8 или 4; и создайте копию corflags.exe соответствующим образом (используя System.Diagnostics.Process) перед запуском ourapp.exe.

Это очень взломанный. Быть осторожен. Очевидно, у вас будет много проблем, если вам понадобится запустить две копии файла ourapp.exe одновременно на одной машине. Было бы идеально создать уникальную копию ourapp.exe в локальную пользовательскую папку перед ее изменением и запустить ее оттуда, чтобы избежать состязаний из нескольких экземпляров и запросов UAC в зависимости от целевой среды.

Является ли абсолютным требованием, чтобы процесс отображался на вкладке " Процессы " диспетчера задач как сам по себе? Или он может работать внутри другого хост-процесса?

Если последнее, вы можете создать проект-заглушку, содержащий следующие 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://stackru.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() метод, мы не передаем никаких параметров Main() метод для приложений WPF, поскольку приложение 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();
Другие вопросы по тегам