Метки диска PowerShell Set сохраняются и остаются неизменными до перезагрузки - PullRequest
2 голосов
/ 08 января 2020

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

Программное обеспечение сначала проверяет, что диск еще не подключен, если это так, то пропускает этап сопоставления.

Если диск не сопоставлен или сопоставлен с другим общим ресурсом (т. Е. Пользователь ранее вошел в другую базу данных), тогда он очищает все существующие сопоставления дисков и сопоставляет требуемый диск.

Это выполняется путем генерации и последующего запуска сценария 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";

Первые три строки удаляют все существующие сопоставления для этой буквы диска. Все они теоретически делают одно и то же, но благодаря Microsoft совершенно случайно, какая линия будет работать. Он работает только последовательно, если все три строки запущены.

Средние три строки отображают новый диск.

Последние две строки изменяют метку диска нового диска на более удобный для пользователя чем по умолчанию \\ server \ share label

При первом входе в систему после перезагрузки вышеуказанный скрипт работает отлично. Новый диск назначен и метка изменена.

Однако, если пользователь затем выйдет из системы и войдет в другую базу данных, метка не изменится.

Например, пользователь сначала регистрируется в «базе данных A», и диск сопоставляется с меткой «DatabaseAFiles». Все хорошо.

Но если затем пользователь выходит из системы и входит в «базу данных B», диск правильно сопоставлен и указывает на правильный общий ресурс, но на этикетке по-прежнему указано «DatabaseAFiles», а не 'DatabaseBFiles'.

Если пользователь, однако, перезагружает свою P C и входит в 'Database B', тогда на метке будет правильно указано 'DatabaseBFiles', но любые последующие входы в другие базы данных снова не будет изменять метку.

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

Независимо от метки, диск всегда правильно сопоставляется с правильный ресурс, и при его использовании все правильные каталоги и файлы.

Все работает правильно, это просто метка, которая неверна после первого входа в систему при перезагрузке.

Сценарий запускается изнутри C# программа в созданном экземпляре PowerShell

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

    IAsyncResult result = PowerShellInstance.BeginInvoke();

    while (result.IsCompleted == false)
    {
        Thread.Sleep(1000);
    }
}

Поскольку он сопоставляет диск, он не может быть запущен в режиме администратора (диск не будет отображен для фактического пользователя ), он должен быть запущен в обычном режиме, поэтому есть проверка для этого ранее.

Если я возьму копию скрипта и запусту его в сеансе PowerShell вне программы C#, я получить точно такие же результаты (все работает, но метка неверна после первого входа в систему), поэтому дело не в том, что она запускается из программы C#.

Вполне возможно, что Он связан с либо Проводником Файла, либо с Windows, либо кеширование метки где-то и повторное его использование, конечно, может быть проблемой.

У кого-нибудь есть предложения, что я могу попробовать, пожалуйста?

Ответы [ 2 ]

0 голосов
/ 31 января 2020

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

Во-первых, я проверяю, есть ли уже диск, сопоставленный с буквой, которую я хочу использовать: -

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

Если у нас уже есть правильный путь, сопоставленный с правильной буквой диска, затем мы возвращаем и пропускаем оставшуюся часть кода сопоставления.

Если нет, у нас будет переменная falseMapping , которая будет be true , если у нас есть другой путь, сопоставленный с желаемой буквой диска. Это означает, что нам нужно сначала отключить этот диск.

Это делается с помощью скрипта Powershell, запускаемого программой C#, и содержит бит, который мне не нравится: -

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 не сможет полностью освободить диск, и, что наиболее досадно, он запомнит метку диска и применит ее к следующему сопоставленному диску (таким образом, при сопоставлении с CompanyBShare все еще говорят CompanyAShare).

Однако при этом он закроет все открытые File Explorer windows, а также на короткое время очистит панель задач, что нехорошо.

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

Как только любой старый диск отключен, мы продолжаем и отображаем новый диск, который снова выполняется с помощью скрипта PowerShell, запускаемого из кода C#.

$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 (если он не существует, он потерпит неудачу, но сценарий продолжится)

Затем он (re) создает свойство _LabelFromReg с новым содружественным именем.

Итак, снова делая то же самое двумя способами, но между двумя оно работает.

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

И по крайней мере, теперь я получаю правильные метки на дисках при отображении.

0 голосов
/ 09 января 2020

Время go, мне пришлось переименовывать файловые ресурсы, и поэтому я написал эту функцию. Может быть, это полезно для вас.

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