Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/272.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# PowerShell设置驱动器标签在重新启动之前保持不变_C#_Powershell_Dictionary_Drive - Fatal编程技术网

C# PowerShell设置驱动器标签在重新启动之前保持不变

C# PowerShell设置驱动器标签在重新启动之前保持不变,c#,powershell,dictionary,drive,C#,Powershell,Dictionary,Drive,我们的软件需要根据用户登录的数据库映射网络驱动器 软件首先检查驱动器是否已映射,如果已映射,则跳过映射步骤 如果驱动器未映射,或映射到其他共享(即用户以前登录到其他数据库),则会清除任何现有驱动器映射,并映射所需的驱动器 它通过生成并运行PowerShell脚本来实现这一点 Remove-SmbMapping -LocalPath "R:" -Force -UpdateProfile; Remove-PSDrive -Name "R" -Force; net use "R" /delete

我们的软件需要根据用户登录的数据库映射网络驱动器

软件首先检查驱动器是否已映射,如果已映射,则跳过映射步骤

如果驱动器未映射,或映射到其他共享(即用户以前登录到其他数据库),则会清除任何现有驱动器映射,并映射所需的驱动器

它通过生成并运行PowerShell脚本来实现这一点

Remove-SmbMapping -LocalPath "R:" -Force -UpdateProfile; 
Remove-PSDrive -Name "R" -Force; 
net use "R" /delete /y; 

$password = ConvertTo-SecureString -String "Password" -AsPlainText -Force; 
$credential = New-Object System.Management.Automation.PSCredential -ArgumentList "Username", $password; 
New-PSDrive -Name "R" -PSProvider "FileSystem" -Root "\\server\share" -Credential $credential -Persist;

$a = New-Object -ComObject shell.application;
$a.NameSpace( "R:" ).self.name = "FriendlyName";
前三行删除该驱动器号上的所有现有映射。理论上,它们都做同样的事情,但多亏了微软,哪一行真正起作用完全是随机的。只有在所有三条线路都运行的情况下,它才能持续工作

中间的三条线表示新驱动器

最后两行将新驱动器的驱动器标签更改为比默认\\server\share标签更方便用户的标签

重新启动后第一次有人登录时,上面的脚本工作正常。将映射新驱动器,并更改标签

但是,如果用户随后注销并登录到其他数据库,则标签不会更改

例如,用户首先登录到“Database A”,驱动器映射为标签“DatabaseAFiles”。一切都很好

但如果用户随后注销并登录到“数据库B”,则驱动器已正确映射并指向正确的共享,但标签上仍然显示“DatabaseAFiles”,而不是“DatabaseBFiles”

但是,如果用户重新启动电脑并登录到“数据库B”,则标签将正确地显示为“数据库文件”,但随后再次登录到其他数据库不会更改标签

Reboot
Log in to Database A, label is DatabaseAFiles
Log out and into Database B, label is still DatabaseAFiles
Reboot
Log in to Database B, label is now DatabaseBFiles
这并不取决于最后两行脚本(设置标签的两行),我实际上添加了这两行脚本来解决这个问题。如果删除了这两行,则标签是默认的\\server\share标签,并且仍然不能正确更改,即

Reboot
Log in to Database A, label is \\servera\sharea
Log out and into Database B, label is still \\servera\sharea
Reboot
Log in to Database B, label is now \\serverb\shareb
不管标签是什么,驱动器总是正确映射到正确的共享,并且使用它时拥有所有正确的目录和文件

一切正常,只是每次重新启动后第一次登录后标签不正确

该脚本在创建的PowerShell实例中的C#程序中运行

using (PowerShell PowerShellInstance = PowerShell.Create())
{
    PowerShellInstance.AddScript(script);

    IAsyncResult result = PowerShellInstance.BeginInvoke();

    while (result.IsCompleted == false)
    {
        Thread.Sleep(1000);
    }
}
当它映射驱动器时,不能在管理员模式下运行(驱动器不会为实际用户映射),它必须在正常模式下运行,因此前面有一个检查

如果我复制一份脚本并在C#程序之外的PowerShell会话中运行它,我会得到完全相同的结果(一切正常,但第一次登录后标签是错误的),因此它不是从C#程序内部运行的

当然,问题完全可能出在文件资源管理器或Windows上,或者将标签缓存在某个位置并重用它都可能是问题所在


有人对我可以尝试的东西有什么建议吗?

不久前,我不得不重命名文件共享,因此我编写了这个函数。也许这对你有帮助

#--------------------------------------
function Rename-NetworkShare {
#--------------------------------------

    param(
        [string]$sharePattern,
        [string]$value
    )

    $regPath      = Get-ChildItem 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2'

    $propertyName = '_LabelFromReg'

    foreach( $child in $regPath ) {

        if( $child.PSChildName -like ('*' + $sharePattern + '*') ) {

            if( !$child.Property.Contains( $propertyName ) ) {
                New-ItemProperty $child.PSPath -Name $propertyName -PropertyType String | Out-Null
            }
            Set-ItemProperty -Path $child.PSPath -Name $propertyName -Value $value | Out-Null
        }
    }
}

Rename-NetworkShare -sharePattern 'patternOldName' -value 'NewFriendlyName'

这并不理想,有一点我不满意,但这是迄今为止我能想到的最好的。如果我能想出更好的办法,我会把它贴出来

首先,我检查是否已经有一个驱动器映射到我要使用的字母:-

// Test if mapping already exists for this database
var wrongMapping = false;
var drives = DriveInfo.GetDrives();
foreach (var drive in drives)
{
    var driveLetter = drive.RootDirectory.ToString().Substring(0, 1);
    if (driveLetter == mappingDetails.DriveLetter && Directory.Exists(drive.Name))
    {
        wrongMapping = true; // Assume this is the wrong drive, if not we'll return from the method before it's used anyway
        var unc = "Unknown";
        using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Network\\" + driveLetter))
        {
            if (key != null)
            {
                unc = key.GetValue("RemotePath").ToString();
            }
        }
        if (unc == mappingDetails.Root)
        {
            View.Status = @"Drive already mapped to " + mappingDetails.DriveLetter + ":";
            ASyncDelay(2000, () => View.Close());
            return; // Already mapped, carry on with login
        }
    }
}
如果已经将正确的路径映射到正确的驱动器号,则返回并跳过其余映射代码

如果不是,我们将有变量错误映射,如果我们有一个不同的路径映射到所需的驱动器号,则该变量将为true。这意味着我们需要先取消映射该驱动器

这是通过C#程序运行的Powershell脚本完成的,其中包含我不满意的内容:-

Remove-PSDrive mappingDetails.DriveLetter;
Remove-SmbMapping -LocalPath "mappingDetails.DriveLetter:" -Force -UpdateProfile;
Remove-PSDrive -Name "mappingDetails.DriveLetter" -Force;
net use mappingDetails.DriveLetter /delete /y;
Stop-Process -ProcessName explorer;
前四行是解除驱动器映射的不同方法,其中至少有一行可以工作。哪一个起作用似乎是随机的,但在所有四个驱动器之间(到目前为止)总是没有映射

然后我们得到这一点:

Stop-Process -ProcessName explorer;
这将关闭并重新启动资源管理器进程,从而迫使Windows承认我们刚刚取消映射的驱动器确实不见了。否则,Windows将无法完全释放驱动器,最令人烦恼的是,它会记住驱动器标签,并将其应用于映射的下一个驱动器(因此,映射到CompanyShare时仍会使用CompanyShare)

但是,这样做会关闭任何打开的文件资源管理器窗口,并短暂清空任务栏,这是不好的

但是,考虑到目前没有一家公司的网站拥有超过一个的份额,只有开发人员和支持人员需要删除现有的驱动器并映射新的驱动器,现在我们可以忍受了

一旦任何旧驱动器被取消映射,我们将继续映射新驱动器,这同样是通过从C#代码运行的PowerShell脚本完成的

第一部分映射驱动器:

$password = ConvertTo-SecureString -String "mappingDetails.Password" -AsPlainText -Force;
$credential = New-Object System.Management.Automation.PSCredential -ArgumentList "mappingDetails.Username", $password;
New-PSDrive -Name "mappingDetails.DriveLetter" -PSProvider "FileSystem" -Root "mappingDetails.Root" -Credential $credential -Persist;
中间部分直接更改名称:

$sh=New_Object -com Shell.Application;
$sh.NameSpace('mappingDetails.DriveLetter:').Self.Name = 'friendlyName';
最后一部分更改注册表中的名称:

New-Item –Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2\" –Name "foldername";
Remove-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2\foldername" -Name "_LabelFromReg";
New-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2\foldername" -Name "_LabelFromReg" -Value "friendlyName" -PropertyType "String\";
首先,它为该路径创建一个密钥(如果该密钥已经存在,它将失败,但脚本将继续)

然后,它将删除现有属性\u LabelFromReg(如果它不存在,它将失败,但脚本将继续)

然后它(重新)使用新的friendlyname创建属性\u LabelFromReg

同样,用两种方法做同样的事情,但在两种方法之间是有效的

我想找到一些替代方法,以避免关闭并重新启动资源管理器进程,这确实很俗气,但这似乎是让Windows确认更改的唯一方法


至少我现在在映射时在驱动器上获得了正确的标签。

我确实看到,一旦创建了共享,它就永远不会被删除。它不可用,但总是在那里。使用
拆下PSJ驱动器
New-Item –Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2\" –Name "foldername";
Remove-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2\foldername" -Name "_LabelFromReg";
New-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2\foldername" -Name "_LabelFromReg" -Value "friendlyName" -PropertyType "String\";