Расширение настраиваемого типа PowerShell путем добавления свойства NoteProperty со значением System.Boolean = $ false с помощью ps1xml

Я пытаюсь расширить тип настраиваемого объекта с помощью настраиваемого файла types.ps1xml. Файл работает и расширяет многие типы так, как я хочу.

Мне удалось расширить настраиваемый тип с помощью NoteProperty с логическим значением $ true. Все, что я хочу сейчас сделать, это добавить NoteProperty к другому типу со значением TypeName System.Boolean и значением $ false. Моя проблема в том, что свойство <value> всегда интерпретируется как строка и при приведении к типу TypeName из System.Boolean ВСЕГДА возвращает $ true, потому что любая строка ненулевой длины возвращает $ true.

Я набираю это на своем телефоне и спешу вернуться к работе, но соответствующие части файла ps1xml следующие:

<Type>
  <Name>My.Custom.Object</Name>
  <Members>
    <NoteProperty>
      <Name>PSIsContainer</Name>
      <TypeName>System.Boolean<TypeName>
      <Value>True</Value>
    </NoteProperty>
  </Members>
</Type>

Это правильно приводит к NoteProperty, который отражает логическое значение $ true. Однако, если я изменю значение на false, или 0, или null, или '', или "", или $ false, или [bool] 0, или любое другое возможное значение с кавычками или без них, с или без $ или внутри $ () или с экранированными кавычками или символами; он ВСЕГДА возвращается как логическое значение $ true. Если я оставлю поле <Value> пустым, при загрузке xml будет выдана ошибка, так что это тоже не сработает. Если я изменю <TypeName> на что-то другое, например System.IO.File или что-то еще, он фактически преобразует значение «false» в файловый объект с именем «false» или в любой другой тип, указанный в <TypeName>. Итак, очевидно, что он работает так, как задумано, за исключением того, что я не могу присвоить логическое значение $ false. Неважно что я делаю. Если я вообще оставлю <TypeName>, по умолчанию будет System.String, и это мне не поможет.

Если вы посмотрите на Get-Member для файла или каталога, вы увидите соответствующее NoteProperty PSIsContainer, равное System.Boolean значению $ true или $ false, в зависимости от; и мой вывод, когда установлен в true, точно такой же, как объект каталога по умолчанию. Это говорит о том, что должна быть возможность установить NoteProperty на $ false.

Я почти забыл, но на самом деле я могу добавить NoteProperty с помощью Add-Member и правильно установить значение $ false, но для работы моего скрипта / инструмента мне нужно, чтобы все экземпляры этого объекта имели это NoteProperty и так использование файла types.ps1xml - единственный способ, который я могу придумать для этого.

Имейте в виду, что объект, для которого я устанавливаю это свойство, является настраиваемым объектом, у которого нет конфликтующих или существующих свойств с таким именем или значением. И не имеет значения, поменяю ли я <Name> на что-нибудь другое, например "IsAwesome"; он по-прежнему не работает для $ false. Я пробовал искать в существующих файлах types.ps1xml и format.ps1xml, и нет никаких намеков на то, как они это делают. Я много дней искал в Интернете и даже просмотрел некоторые учебники по Powershell, но безрезультатно.

Кто-нибудь может мне помочь !?


person Jessie Westlake    schedule 29.12.2015    source источник
comment
@PetSerAl Я попробую это утром. Хотя я хотел бы знать, как, если это вообще возможно, сделать это в файле конфигурации, но если это сработает, я тем не менее буду очень счастлив.   -  person Jessie Westlake    schedule 29.12.2015


Ответы (1)


Я не думаю, что это возможно. Я использовал ILSpy, чтобы изучить Microsoft.PowerShell.Commands.Utility сборку, где находится UpdateTypeDataCommand командлет. Мне кажется, что он использует метод ProcessNote из Runspaces.TypeTable для добавления свойств заметки. Вот соответствующая часть из него:

// System.Management.Automation.Runspaces.TypeTable
private static void ProcessNote(
    LoadContext context,
    string typeName,
    Node node,
    PSMemberInfoInternalCollection<PSMemberInfo> membersCollection,
    Collection<int> nodeLineNumbers
)
{

...

if (actualNodes.Count == 1)
{
    if (actualNodes[0].nodeError)
    {
        return;
    }
    Type typeFromString = TypeTable.GetTypeFromString(
        context,
        typeName,
        actualNodes[0]
    );
    if (typeFromString == null)
    {
        return;
    }
    try
    {
        obj = LanguagePrimitives.ConvertTo(
            obj,
            typeFromString,
            CultureInfo.InvariantCulture
        );
    }
    catch (PSInvalidCastException ex)
    {
        context.AddError(typeName,
            actualNodes[0].lineNumber,
            TypesXmlStrings.Exception,
            new object[] {ex.Message}
        );
        return;
    }
}

PSNoteProperty pSNoteProperty = new PSNoteProperty(node2.innerText, obj);
pSNoteProperty.isHidden = (node.isHidden.HasValue && node.isHidden.Value);
TypeTable.AddMember(context,
    typeName,
    node.lineNumber,
    pSNoteProperty,
    membersCollection, 
    nodeLineNumbers
);

Если вы укажете <TypeName> в XML, он попытается преобразовать в него значение NoteProperty. И поскольку исходное значение является строкой, он выглядит так, как вызывается этот метод:

// System.Management.Automation.LanguagePrimitives
private static bool ConvertStringToBool(
    object valueToConvert,
    Type resultType,
    bool recursion,
    PSObject originalValueToConvert,
    IFormatProvider formatProvider,
    TypeTable backupTable
)
{
    LanguagePrimitives.typeConversion.WriteLine(
        "Converting string to boolean.",
        new object[0]
    );
    return LanguagePrimitives.IsTrue((string)valueToConvert);
}

А тело метода LanguagePrimitives.IsTrue очень простое:

// System.Management.Automation.LanguagePrimitives
internal static bool IsTrue(string s)
{
    return s.Length != 0;
}

Если строка не пуста, она преобразуется в true. А поскольку у вас не может быть пустого узла <Value> в XML (Update-TypeData : Error: The node Value is not allowed.), это фактически делает невозможным создание NoteProperty с false логическим значением.

P.S. Хотя это соответствует наблюдаемому поведению, это все еще в основном предположения, поэтому не стесняйтесь поправлять меня, если я ошибаюсь.

person beatcracker    schedule 29.12.2015
comment
Я просто понял, что никогда не благодарил тебя за этот ответ. Несмотря на то, что это не решило мою проблему, я считаю, что ваш ответ точен на 100%, поскольку вы не можете использовать XML-файл пользовательского типа для выполнения того, что я хотел. Это позор, и, казалось бы, большое отсутствие предвидения со стороны MSFT. Тем не менее, спасибо за информативную информацию. - person Jessie Westlake; 09.06.2016