我正在编写Powershell脚本来卸除连接的USB磁盘,之后它将运行一些PS代码,然后应该再次安装USB驱动器。
Powershell脚本适用于Windows 7以上版本和MS Server 2012以上版本。我认为安装弹出的USB磁盘的最佳方法是通过在设备管理器中禁用/启用USB大容量存储设备,因为此后可以再次安装USB磁盘。从主面板上卸下USB磁盘后,设备管理器中适当的USB大容量存储设备会将其状态更改为安全移除(错误47),因此根据该“错误”,可以轻松识别USB磁盘。
https://www.thewindowsclub.com/remount-ejected-usb-drive-windows
#Identify connected USB disk/s and dismounting
$usbDrives = @(Get-CimInstance -Class Win32_DiskDrive -Filter 'InterfaceType = "USB"' -KeyOnly | Get-CimAssociatedInstance - ResultClassName Win32_DiskPartition -KeyOnly | Get-CimAssociatedInstance - ResultClassName Win32_LogicalDisk | ForEach-Object{$_.deviceid})
for ($i = 0; $i -le ($usbDrives.length - 1); $i += 1) {
$Eject = New-Object -comObject Shell.Application
$Eject.NameSpace(17).ParseName($usbDrives[$i]).InvokeVerb("Eject")
}
#Some code...
#Getting USB Mass Storage Devices with error 47 and disable/enable
Get-WmiObject Win32_PNPEntity | Where-Object{$_.ConfigManagerErrorCode -eq 47} | Select-Object Name, DeviceID | ForEach-Object {
$_ | Disable-PnpDevice -Confirm:$false;
$_ | Enable-PnpDevice -Confirm:$false;
}
如果我使用脚本弹出已连接的USB磁盘,则USB磁盘将断开连接,但设备管理器中的USB Mass Storage Device不会将其状态更改为错误47,其状态将保持连接状态,我可以不能挂接连接的USB磁盘。
请认识某人,如何编写或修复此PS代码以卸下和安装连接的USB磁盘?
谢谢
答案 0 :(得分:0)
要将USB大容量存储设备的状态通过编程方式更改为错误47 ,我们需要为设备的CM_DEVCAP_REMOVABLE
位设置为Capabilities
的设备调用CM_Request_Device_Eject
function {1}}属性。我们可以验证它是USB驱动器本身的父级(例如,使用 Config Manager )。
要从Windows中的 PowerShell 中执行此操作,请定义并调用本机Windows API(例如,参见Example 4 in the Add-Type cmdlet documentation)。
首先,使用以下基本功能定义[Usb.Api]
类型:
CM_Locate_DevNode
-获取设备树的根节点。 CM_Get_Parent
-获取设置了CM_DEVCAP_REMOVABLE
位的设备ID(重要!)。CM_Request_Device_Eject
-按键功能可让您安全地卸下设备。然后,按如下方式使用它(在56496356a.ps1
脚本中,我省略了 disable / enable 部分,因为它需要提高会话强度,另请参见56496356b.ps1
和56496356c.ps1
下面的附录中的脚本)
# 56496356a.ps1
Function Remove-USBSafely {
[CmdletBinding()]
param (
[Parameter(Mandatory,ValueFromPipeline)]
[PSObject]$usbDrive # necessary: a `PNPDeviceID` property
)
Begin {
try { $null = [Usb.Api] }
catch { . D:\PShell\tests\Set-UsbApi.ps1 } # change path to match your circumstances
}
Process {
$DevInst = $DevInstParent = 0
# get the root node of the device tree
$DevNodeA = [Usb.Api]::CM_Locate_DevNode(
[ref]$devinst,$usbDrive.PNPDeviceID,0)
# get the device ID; Capabilities bit CM_DEVCAP_REMOVABLE (Important!)
$DevNodeP = [Usb.Api]::CM_Get_Parent(
[ref]$DevInstParent,$devinst,0)
# safely remove the device
try {
$aux = [Usb.Api]::Eject($DevInstParent)
switch ( $aux ) {
"OK" { Write-Host -ForegroundColor Yellow "Success $aux`: $usbDrive";
return 0 } # device removed
default { Write-Host -ForegroundColor Cyan "Fail $aux`: $usbDrive";
return -1 } # failed: maybe some locked files?
}
}
catch {
Write-Host -ForegroundColor Red "Blocked $usbDrive";
return -5 } # failed: maybe some locked files?
}
}
# Identify connected USB disk/s
$usbDrives = @(
Get-CimInstance Win32_DiskDrive -Filter 'InterfaceType = "USB"' -OutVariable DiskDrive -PipelineVariable Disk |
Get-CimAssociatedInstance -ResultClassName Win32_DiskPartition -OutVariable DiskPartition -PipelineVariable Part |
Get-CimAssociatedInstance -ResultClassName Win32_LogicalDisk -OutVariable DiskLogical |
Select-Object `
@{n='DriveLetter'; e={$_.DeviceID}},
@{n='PNPDeviceID'; e={$Disk.PNPDeviceID}},
# collect the following properties merely for debugging purposes
@{n='VolumeName' ; e={$_.VolumeName}},
@{n='DiskModel' ; e={$Disk.model}},
@{n='Disk' ; e={$Disk.deviceid}},
@{n='Partition' ; e={$Part.name}}
)
$usbDrives | ForEach-Object {
$auxResult = $_ | Remove-USBSafely
if ( $auxResult -ne 0 ) {
### eject the drive anyway ?
# $Eject = New-Object -comObject Shell.Application
# $Eject.NameSpace(17).ParseName($_.DriveLetter).InvokeVerb("Eject")
}
}
#Some code...
[Usb.Api]
类型在Set-UsbApi.ps1
脚本中定义如下:
Set-StrictMode -Version latest
# D:\PShell\tests\Set-UsbApi.ps1
try { $null = [Usb.Api] }
catch { # C# signature (pinvoke)
$script:UsbApiCode = @"
using System;
using System.Text;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace Usb
{
public class Api
{
public enum PNP_VETO_TYPE
{
Ok,
TypeUnknown,
LegacyDevice,
PendingClose,
WindowsApp,
WindowsService,
OutstandingOpen,
Device,
Driver,
IllegalDeviceRequest,
InsufficientPower,
NonDisableable,
LegacyDriver,
InsufficientRights
}
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
static extern int CM_Request_Device_Eject(
IntPtr devinst,
out PNP_VETO_TYPE pVetoType,
System.Text.StringBuilder pszVetoName,
int ulNameLength,
int ulFlags); // 0 (not used)
[DllImport("setupapi.dll", SetLastError=true)]
public static extern int CM_Locate_DevNode(
ref int pdnDevInst,
string pDeviceID,
int ulFlags); // 0..4, 7
[DllImport("setupapi.dll")]
public static extern int CM_Get_Parent(
out UInt32 pdnDevInst,
UInt32 dnDevInst,
int ulFlags); // Not used, must be zero
public static string Eject(int devinst)
{
StringBuilder sb = new StringBuilder(255);
PNP_VETO_TYPE veto;
IntPtr dev = new IntPtr(devinst);
int hr = CM_Request_Device_Eject(dev, out veto, sb, sb.Capacity, 0);
if (hr != 0)
throw new Win32Exception(hr);
return veto.ToString();
}
// JosefZ 2019-06-10
[DllImport("setupapi.dll")]
public static extern int CM_Disable_DevNode(
UInt32 dnDevInst,
int ulFlags); // 0..4, 8, 0xF
[DllImport("setupapi.dll")]
public static extern int CM_Enable_DevNode(
UInt32 dnDevInst,
int ulFlags); // must be 0
/*
The CM_Setup_DevNode function restarts a device instance that is
not running because there is a problem with the device configuration.
*/
[DllImport("setupapi.dll")]
public static extern int CM_Setup_DevNode(
UInt32 dnDevInst,
int ulFlags); // 0 or 4
}
public enum CONFIGRET
{
CR_SUCCESS , // 0x00
CR_DEFAULT , // 0x01
CR_OUT_OF_MEMORY , // 0x02
CR_INVALID_POINTER , // 0x03
CR_INVALID_FLAG , // 0x04
CR_INVALID_DEVNODE , // 0x05 // CR_INVALID_DEVINST
CR_INVALID_RES_DES , // 0x06
CR_INVALID_LOG_CONF , // 0x07
CR_INVALID_ARBITRATOR , // 0x08
CR_INVALID_NODELIST , // 0x09
CR_DEVNODE_HAS_REQS , // 0x0A // CR_DEVINST_HAS_REQS
CR_INVALID_RESOURCEID , // 0x0B
CR_DLVXD_NOT_FOUND , // 0x0C // WIN 95 ONLY
CR_NO_SUCH_DEVNODE , // 0x0D // CR_NO_SUCH_DEVINST
CR_NO_MORE_LOG_CONF , // 0x0E
CR_NO_MORE_RES_DES , // 0x0F
CR_ALREADY_SUCH_DEVNODE , // 0x10 // CR_ALREADY_SUCH_DEVINST
CR_INVALID_RANGE_LIST , // 0x11
CR_INVALID_RANGE , // 0x12
CR_FAILURE , // 0x13
CR_NO_SUCH_LOGICAL_DEV , // 0x14
CR_CREATE_BLOCKED , // 0x15
CR_NOT_SYSTEM_VM , // 0x16 // WIN 95 ONLY
CR_REMOVE_VETOED , // 0x17
CR_APM_VETOED , // 0x18
CR_INVALID_LOAD_TYPE , // 0x19
CR_BUFFER_SMALL , // 0x1A
CR_NO_ARBITRATOR , // 0x1B
CR_NO_REGISTRY_HANDLE , // 0x1C
CR_REGISTRY_ERROR , // 0x1D
CR_INVALID_DEVICE_ID , // 0x1E
CR_INVALID_DATA , // 0x1F
CR_INVALID_API , // 0x20
CR_DEVLOADER_NOT_READY , // 0x21
CR_NEED_RESTART , // 0x22
CR_NO_MORE_HW_PROFILES , // 0x23
CR_DEVICE_NOT_THERE , // 0x24
CR_NO_SUCH_VALUE , // 0x25
CR_WRONG_TYPE , // 0x26
CR_INVALID_PRIORITY , // 0x27
CR_NOT_DISABLEABLE , // 0x28
CR_FREE_RESOURCES , // 0x29
CR_QUERY_VETOED , // 0x2A
CR_CANT_SHARE_IRQ , // 0x2B
CR_NO_DEPENDENT , // 0x2C
CR_SAME_RESOURCES , // 0x2D
CR_NO_SUCH_REGISTRY_KEY , // 0x2E
CR_INVALID_MACHINENAME , // 0x2F // NT ONLY
CR_REMOTE_COMM_FAILURE , // 0x30 // NT ONLY
CR_MACHINE_UNAVAILABLE , // 0x31 // NT ONLY
CR_NO_CM_SERVICES , // 0x32 // NT ONLY
CR_ACCESS_DENIED , // 0x33 // NT ONLY
CR_CALL_NOT_IMPLEMENTED , // 0x34
CR_INVALID_PROPERTY , // 0x35
CR_DEVICE_INTERFACE_ACTIVE , // 0x36
CR_NO_SUCH_DEVICE_INTERFACE , // 0x37
CR_INVALID_REFERENCE_STRING , // 0x38
CR_INVALID_CONFLICT_LIST , // 0x39
CR_INVALID_INDEX , // 0x3A
CR_INVALID_STRUCTURE_SIZE , // 0x3B
NUM_CR_RESULTS , // 0x3C
}
}
"@
Add-Type -TypeDefinition $script:UsbApiCode
Remove-Variable -Name UsbApiCode -Scope script
}
附录-尝试将USB设备恢复到其运行状态失败:注释脚本:
56496356b.ps1
使用Disable-PnpDevice
和Enable-PnpDevice
cmdlet
#Requires -RunAsAdministrator
Begin
{
Import-Module PnpDevice
}
Process
{
#Getting USB Mass Storage Devices with error 47 and disable/enable
Get-WmiObject Win32_PNPEntity | Where-Object{$_.ConfigManagerErrorCode -eq 47} |
ForEach-Object {
Write-Host $_.Caption -ForegroundColor Cyan
$_ | Disable-PnpDevice -Confirm:$false; ### #Requires -RunAsAdministrator
Write-Host $_.DeviceID -ForegroundColor Yellow
pause # Device manager shows `Enable Device` in the right click menu
# this means that the device was succesfully disabled
$_ | Enable-PnpDevice -Confirm:$false;
# Device manager shows `Disable Device` in the right click menu
}
}
56496356c.ps1
使用CM_Disable_DevNode
类的CM_Enable_DevNode
,CM_Setup_DevNode
和[Usb.Api]
函数:
#Requires -RunAsAdministrator
begin {
try { $null = [Usb.Api] }
catch { . D:\PShell\tests\Set-UsbApi.ps1 }
Import-Module PnpDevice
}
Process {
#Getting USB Mass Storage Devices with error 47 and disable/enable
Get-WmiObject Win32_PNPEntity | Where-Object{$_.ConfigManagerErrorCode -eq 47} |
ForEach-Object {
Write-Host $_.Caption -ForegroundColor Cyan
$DevInst = 0
[Usb.CONFIGRET]([Usb.Api]::CM_Locate_DevNode( [ref]$DevInst, $_.PNPDeviceID, 0))
[Usb.CONFIGRET]([Usb.Api]::CM_Disable_DevNode( $DevInst, 0)) # using any supported flag
Write-Host $_.DeviceID -ForegroundColor Yellow
# Device manager shows `Disable Device` constantly in the right click menu
# this means that the device was NOT disabled keeping its `Enable` state
# pause
[Usb.CONFIGRET]([Usb.Api]::CM_Enable_DevNode( $DevInst, 0))
# pause
[Usb.CONFIGRET]([Usb.Api]::CM_Setup_DevNode( $DevInst, 8)) # using any supported flag
}
}
确认。非常感谢Александр AKA Kazun在Safely remove USB flash (Безопасное извлечение USB Flash in Russian original)文章中的操作复杂脚本。