【问题标题】:PowerShell Set Drive Labels Persisting And Unchangeable Until RebootPowerShell 设置驱动器标签在重启前保持不变
【发布时间】:2020-04-26 07:01:38
【问题描述】:

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

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

如果驱动器未映射,或者它被映射到不同的共享(即用户之前登录到不同的数据库),那么它会清除任何现有的驱动器映射,并映射所需的驱动器。

它通过生成然后运行一个 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”。

但是,如果用户重新启动其 PC 并登录到“数据库 B”,则标签将正确显示“数据库 BFiles”,但任何后续再次登录到其他数据库都不会更改标签。

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 上,当然,将标签缓存在某处并重复使用它可能是问题所在。

有人对我可以尝试的事情有什么建议吗?

【问题讨论】:

  • 我确实看到,一旦您创建了共享,它就永远不会被删除。它不可用,但总是在那里。在其他人之前先使用Remove-PSDrive J。(不带引号或参数)看看是否有帮助
  • 我尝试在不带引号或其他参数的情况下添加 Remove-PSDrive R,首先在其他删除行之前,但不幸的是它仍然记得名称。不过谢谢!

标签: c# powershell dictionary drive


【解决方案1】:

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

#--------------------------------------
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'

【讨论】:

  • 我尝试直接在 PowerShell 中运行它,而不是从我的程序中运行,只是为了看看它是如何进行的。第一次,它确实重命名了它。第二次,它在文件资源管理器的“这台电脑”列表中显示新名称,但在左侧列表中显示旧名称。第三次,以及以后的任何时候,它都没有改变任何名称。不过谢谢,它给了我一些可以玩和尝试的东西。
【解决方案2】:

这并不理想,有一点我不满意,但这是迄今为止我能想到的最好的。如果我想出更好的东西,我会改为发布。

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

// 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
        }
    }
}

如果我们已经将正确的路径映射到正确的驱动器号,则返回并跳过其余的映射代码。

如果不是,我们将拥有变量 wrongMapping,如果我们将不同的路径映射到所需的驱动器号,则该变量将为 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;

这将关闭并重新启动 Explorer 进程,从而迫使 Windows 承认我们刚刚取消映射的驱动器真的消失了。没有这个,Windows 将不会完全释放驱动器,最烦人的是它会记住驱动器标签并将其应用到下一个映射的驱动器(因此映射到 CompanyBShare 仍然是 CompanyAShare)。

但是,这样做会关闭所有打开的 文件资源管理器 窗口,并且还会短暂地空白任务栏,这并不好。

但是,鉴于目前没有任何公司网站拥有超过一个份额,只有开发人员和支持人员需要删除现有驱动器并映射新驱动器,现在我们将忍受它。

一旦取消映射任何旧驱动器,我们就会继续并映射新驱动器,这又是通过从 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\";

第一部分映射驱动器:

$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\";

首先,它为此路径创建一个密钥(如果密钥已经存在,它将失败,但脚本将继续执行)

然后它会删除现有的属性 _LabelFromReg(如果它不存在,它会失败,但脚本会继续执行)

然后它(重新)使用新的友好名称创建属性 _LabelFromReg

所以,再次以两种方式做同样的事情,但在两者之间它有效。

我想找到一些替代方法来终止并重新启动 Explorer 进程,这确实很俗气,但它似乎是让 Windows 确认更改的唯一方法。

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

【讨论】:

    猜你喜欢
    • 2011-08-25
    • 2012-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    相关资源
    最近更新 更多