Загрузка модуля 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 поворачивается и загружает нового провайдера, поэтому, если я снова вручную удаляю и импортирую модуль, он делает свое дело, и у меня есть все функции привода для моего провайдера.
Я хотел знать после этого ДОЛГОГО объяснения, как я могу либо:
Я не хочу удалять его из моего стандартного профиля, потому что это очень полезно, когда я не работаю с новым провайдером и просто использую 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. (Пустышка будет последней.)