MSBuild не видит переменную среды, заданную в рабочем процессе сборки

Это связано с системой сборки TFS 2010. В нашем рабочем процессе сборки мы устанавливаем пару переменных среды, используя метод SetEnvironmentVariable класса System.Environment. Я подтвердил, что эта переменная среды правильно устанавливается на сервере сборки и устанавливается как общесистемная переменная среды.

Проблема в том, что когда MSBuild вызывается в этом WF и компилирует решения, наши события пост-сборки, которые пытаются прочитать эту переменную среды, терпят неудачу, поскольку они не могут видеть эту переменную среды.

Есть ли способ заставить MSBuild перезагрузить переменные среды или заставить работающий WF перезагрузить переменные среды? Я подозреваю, что даже несмотря на то, что WF создает эту переменную, он не обновляет свое состояние среды и, следовательно, не может видеть переменную. Кроме того, поскольку WF вызывает MSBuild, он передает то же состояние среды в MSBuild, которое не содержит эту переменную.

Обновить

Вставьте следующий код в Visual Studio и запустите его. Задержка SendMessageTimeOut составляет 10 секунд, так что наберитесь терпения.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace EnvironmentVarTest
{
    class Program
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool
        SendMessageTimeout(
            IntPtr hWnd,
            int Msg,
            int wParam,
            string lParam,
            int fuFlags,
            int uTimeout,
            out int lpdwResult
        );

        public const int HWND_BROADCAST = 0xffff;
        public const int WM_SETTINGCHANGE = 0x001A;
        public const int SMTO_NORMAL = 0x0000;
        public const int SMTO_BLOCK = 0x0001;
        public const int SMTO_ABORTIFHUNG = 0x0002;
        public const int SMTO_NOTIMEOUTIFNOTHUNG = 0x0008;


        static void Main(string[] args)
        {
            Program p = new Program();
            string environmentVariableValue = DateTime.Now.ToLongTimeString().Replace(":", String.Empty);
            Console.WriteLine("On the CMD window that opens up after about 10 seconds, if you type %samplevar% and hit Enter, you should see: " + environmentVariableValue);
            p.SetEnvironmentVariable(environmentVariableValue);
            RefreshProcessVars();
            p.ReadEnvironmentVariable();

            p.StartCMD();
            Console.ReadLine();
        }

        void SetEnvironmentVariable(string value)
        {
            System.Environment.SetEnvironmentVariable("samplevar", value, EnvironmentVariableTarget.Machine);

        }

        static void RefreshProcessVars()
        {
            int result;
            bool callresult = SendMessageTimeout(
                 (System.IntPtr)HWND_BROADCAST,
                 WM_SETTINGCHANGE,
                 0,
                 "Environment",
                 SMTO_BLOCK | SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG,
                 10000,
                 out result);

            if (!callresult || result == 0)
            {
                int lasterror = Marshal.GetLastWin32Error();
                Win32Exception winex = new Win32Exception(lasterror);
                Console.WriteLine("Exception happened while calling SendMessageTimeOut. The exception message is " + winex.Message);
            }
        }
        void ReadEnvironmentVariable()
        {
            var x = System.Environment.GetEnvironmentVariable("smaplevar", EnvironmentVariableTarget.Machine);

        }

        void StartCMD()
        {
            Process.Start("cmd.exe");
        }
    }
}

person Nikhil    schedule 11.09.2012    source источник


Ответы (1)


К сожалению, поскольку процессу MSBuild при запуске передается кэшированная версия переменных среды, функции командной строки этого процесса не смогут увидеть обновленные значения. На мой взгляд, лучший вариант, который у вас есть, - это либо изменить эту переменную в событии после сборки, либо сохранить значение на носителе, который вы можете прочитать из события после сборки.

Обновлять

Итак, приведенное ниже утверждение (которое можно найти здесь) Я думаю, это объясняет, откуда берутся переменные среды и почему вы не получаете обновленную версию в MSBuild.

По умолчанию дочерний процесс наследует переменные среды своего родительского процесса.

Итак, в эту игру могут играть двое, давайте просто покажем, что произошли изменения, и посмотрим, поможет ли это нам. Ниже приведен код, который должен сделать это за вас.

[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
[return:MarshalAs(UnmanagedType.Bool)]
public static extern bool
SendMessageTimeout(
    IntPtr hWnd,
    int Msg,
    int wParam,
    string lParam,
    int fuFlags,
    int uTimeout,
    out int lpdwResult
);

public const int HWND_BROADCAST = 0xffff;
public const int WM_SETTINGCHANGE = 0x001A;
public const int SMTO_NORMAL = 0x0000;
public const int SMTO_BLOCK = 0x0001;
public const int SMTO_ABORTIFHUNG = 0x0002;
public const int SMTO_NOTIMEOUTIFNOTHUNG = 0x0008;

int result;
SendMessageTimeout(
    (System.IntPtr)HWND_BROADCAST,
    WM_SETTINGCHANGE,
    0,
    "Environment",
    SMTO_BLOCK | SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG,
    SomeTimeoutValue,
    out result);
person Mike Perrenoud    schedule 11.09.2012
comment
Спасибо, Майк. Нет ли способа заставить сборку WF обновлять кеш переменных среды? - person Nikhil; 11.09.2012
comment
Мне еще предстоит найти этот API. Есть вероятность, что в Windows есть недокументированный API, но в настоящее время Windows по-прежнему передает кешированную версию переменных в каждый новый процесс. И поскольку рабочий процесс - это другой процесс, чем MSBUILD, они теряют синхронизацию по мере изменения вещей. - person Mike Perrenoud; 11.09.2012
comment
Вопрос в том, что когда WF вызывает MSBuild, передает ли он ту же копию переменных env, что и при запуске? Если нет, то MSBuild наверняка сможет увидеть новую переменную среды, созданную WF. Имеет ли это смысл? - person Nikhil; 13.09.2012
comment
@Nikhil: ты поднял очень хороший вопрос. Позвольте мне немного поразмышлять над этим и посмотреть, сможем ли мы продолжать двигаться по пути к решению. - person Mike Perrenoud; 13.09.2012
comment
@Nikhil: Я обновил свой ответ, дайте мне знать, работает ли он для вас. - person Mike Perrenoud; 13.09.2012
comment
Спасибо, Майк. Я смоделировал тестовую оснастку с вашим образцом кода, чтобы поэкспериментировать с ней. Однако, что бы я ни делал, у меня возникают тайм-ауты, и переменная среды не обновляется. Хотите посмотреть, что происходит? Я вставил код с пояснением - person Nikhil; 13.09.2012
comment
С удовольствием посмотрю, как только смогу. Спасибо, что заглянули к тесту! - person Mike Perrenoud; 13.09.2012
comment
Спасибо. Дайте мне знать, что вы найдете, пожалуйста. - person Nikhil; 14.09.2012
comment
@Nikhil: У меня нет таймаутов, но я являюсь администратором на своем локальном компьютере, поэтому может появиться предупреждение о том, что для этой функции потребуются повышенные разрешения. Более того, на данный момент он не обновляет кешированную версию, которую окно CMD все равно получает, поэтому я буду работать с ней как можно быстрее. - person Mike Perrenoud; 14.09.2012
comment
Привет, Майк, мне было интересно, есть ли у тебя какой-нибудь прорыв в этом вопросе? - person Nikhil; 19.09.2012
comment
@Nikhil, я не могу найти способ. Извини, я должен был ответить тебе. Если найдете способ, отредактируйте, пожалуйста, мой пост. - person Mike Perrenoud; 11.10.2012