从自定义提供程序的c#代码中加载Powershell模块

本文关键字:代码 加载 Powershell 模块 自定义 程序 | 更新日期: 2023-09-27 18:14:29

我一直在研究一个非常具体的功能"需要",以绑定到我正在用c#编写的自定义提供程序。

基本上,我开始寻找一种方法来复制

A:
B:

etc函数是在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路径下的自己的文件夹

最后我有个人资料。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模块加载LAST
  • 找到一种方法来让我的自定义提供程序(技术上是一个模块,因为它有提供程序和自定义cmdlet)加载UseColons模块时,它初始化
  • 我不想把它从我的标准配置文件中删除,因为当我不在新的提供商上工作时,它非常有用,只是在管理方面使用powershell。

    希望有人能给我一些想法或指出我在一些好的更深入的潜水powershell提供程序文档和如何操作的方向。

    从自定义提供程序的c#代码中加载Powershell模块

    您可以通过覆盖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 }
    

    如果命令已经存在,则为-ErrorAction Ignore

    在您的模块清单(.psd1)中,您有一个DLL作为RootModule?

    这是一个可怕的黑客,并且对将来创建的驱动器没有帮助,但是…

    在你的模块清单中,代替YourProvider.dll作为RootModule,使用Dummy。Psm1代替(可以是一个空文件)。然后,对于NestedModules,使用@( 'YourProvider.dll', 'UseColons' )。这允许UseColons模块在 YourProvider.dll之后加载。(Dummy是最后一个)