Загрузка модуля Powershell из кода C# пользовательского провайдера

Я работал над ОЧЕНЬ специфической функциональностью, "необходимой", чтобы связать ее с пользовательским провайдером, который я пишу на C#.

По сути, я решил найти способ воспроизвести

A:
B:

и т. д. функции, определенные при загрузке PowerShell, вместо того, чтобы вводить

CD A:

Вы можете просто сделать вышеупомянутое

A:

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

В основном у меня очень простой файл PSM1 UseColons.psm1

function Use-ColonsForPSDrives
{
    [CmdletBinding()] Param()

    Write-Verbose "Looping Through Installed PowerShell Providers"
    Get-PSProvider | % `
    {
        Write-Verbose "Found $($_.Name) checking its drives"
        $_.Drives | ? { (Get-Command | ? Name -eq "$($_.Name):") -eq $null } | `
        { 
            Write-Verbose "Setting up: `"function $($_.Name):() {Set-Location $($_.Name):}`""
            if ($Verbose)
            {
                . Invoke-Expression -Command "function $($_.Name):() {Set-Location $($_.Name):}" 
            }
            else
            {
                . Invoke-Expression -Command "function $($_.Name):() {Set-Location $($_.Name):}" -ErrorAction SilentlyContinue
        }
            Write-Verbose "Finished with drive $($_.Name)"
        }
    }
    # Cert and WSMan do not show up as providers until you try to naviagte to their drives
    # As a result we will add their functions manually but we will check if they are already set anyways
    if ((Get-Command | ? Name -eq "Cert:") -eq $null) { . Invoke-Expression -Command "function Cert:() {Set-Location Cert:}" }
    if ((Get-Command | ? Name -eq "WSMan:") -eq $null) { . Invoke-Expression -Command "function WSMan:() {Set-Location WSMan:}" }
}

. Use-ColonsForPSDrives

Проще говоря, он проходит по всем загруженным провайдерам, затем по всем дискам каждого провайдера, затем проверяет, содержит ли диск Function: функцию, соответствующую формату {DriveName}: и, если она не найдена, она ее создает.

Файл psd1 - это не что иное, как экспорт всех функций.

Это хранится в пути%ProgramFiles%\WindowsPowerShell\Modules в его собственной папке

И, наконец, у меня есть файл profile.ps1 в каталоге%windir%\system32\windowspowershell\v1.0, который просто выполняет

Remove-Module UseColons -ErrorAction SilentlyContinue
Import-Module UseColons

Поэтому, когда я загружаю PowerShell или ISE, если я хочу сказать dir через переменные, я могу просто вызвать

Variable:

Или если мне нужно переключиться обратно в реестр

HKLM:
HKCU:

Что, когда вы работаете с несколькими провайдерами, печатая этот CD снова и снова при переключении, просто раздражает.

Что касается проблемы, я все еще работаю над разработкой фактического поставщика PowerShell, для которого он изначально был предназначен. Но когда я отлаживаю его, модуль UseColons загружается ПЕРЕД тем, как visual studio поворачивается и загружает нового провайдера, поэтому, если я снова вручную удаляю и импортирую модуль, он делает свое дело, и у меня есть все функции привода для моего провайдера.

Я хотел знать после этого ДОЛГОГО объяснения, как я могу либо:

  • Установите мой модуль UseColons для загрузки ПОСЛЕДНЕМ
  • Найдите способ, чтобы мой пользовательский поставщик (технически модуль, поскольку у него есть поставщик и пользовательские командлеты) загружал модуль UseColons при его инициализации
  • Я не хочу удалять его из моего стандартного профиля, потому что это очень полезно, когда я не работаю с новым провайдером и просто использую PowerShell для административных задач.

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

    2 ответа

    Вы можете динамически создавать новую функцию каждый раз, когда к вашему провайдеру добавляется новый диск, переопределяяNewDriveтакой метод:

          protected override PSDriveInfo NewDrive(PSDriveInfo drive)
    {
        InvokeCommand.InvokeScript($"New-Item -Path 'Function:\\' -Name 'global:{drive.Name}:' -Value {{ Set-Location $MyInvocation.MyCommand.Name }}");
        return drive;
    }
    

    Таким образом, диски вашего провайдера имеют ту же функциональность, что и диски встроенной файловой системы:

          Get-Item "Function:\C:" | select Name, ScriptBlock
    

    вернется:

          Name ScriptBlock
    ---- -----------
    C:   Set-Location $MyInvocation.MyCommand.Name
    

    Вы можете добавить аналогичную команду вRemoveDriveчтобы снова удалить функцию.

    Конечно, это будет работать только для дисков вашего провайдера. Если вы хотите сделать это для других провайдеров, вы можете запустить эту команду:

          Get-PSDrive | ForEach-Object { New-Item -Path "Function:\\" -Name "global:$($_.Name):" -Value { Set-Location $MyInvocation.MyCommand.Name } -ErrorAction Ignore }
    

    The -ErrorAction Ignoreв случае, если команда уже существует.

    В вашем модуле манифест (.psd1), у вас есть DLL как RootModule?

    Это ужасный взлом, и он не помогает для дисков, которые будут созданы в будущем, но...

    В манифесте вашего модуля вместо YourProvider.dll в качестве RootModule используйте вместо этого Dummy.psm1 (может быть пустым файлом). Затем для NestedModules используйте @( 'YourProvider.dll', 'UseColons' ), Это позволяет загружать модуль UseColons после YourProvider.dll. (Пустышка будет последней.)

    Другие вопросы по тегам