Использование коммандлета Add-Type для вызова функции Windows API
Коммандлет Add-Type добавляет заданный .NET класс в текущий сеанс PowerShell. Данная статья демонстрирует использование этого командлета для доступа к функции CopyFile, объявленной в библиотеке kernel32.dll.
Базовая реализация
Рассмотрим скрипт, вызывающий функцию CopyFile
:
$MethodDefinition = @'
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
public static extern bool CopyFile(string lpExistingFileName, string lpNewFileName, bool bFailIfExists);
'@
$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -Namespace 'Win32' -PassThru
$Kernel32::CopyFile("$($Env:SystemRoot)\System32\calc.exe", "$($Env:USERPROFILE)\Desktop\calc.exe", $False)
Этот скрипт делает следующее:
-
Переменная
$MethodDefinition
содержит определение метода на C#, соответствующее сигнатуреCopyFile
на C++:Разверните, чтобы увидеть подробности
Сигнатура метода на C++BOOL CopyFile( [in] LPCTSTR lpExistingFileName, [in] LPCTSTR lpNewFileName, [in] BOOL bFailIfExists );
Определение метода на C#[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool CopyFile( string lpExistingFileName, string lpNewFileName, bool bFailIfExists );
Аттрибут DllImport указывает что метод
CopyFile
доступен в библиотеке kernel32.dll в качестве статической точки входа.Обратите внимание на корректность соответствия типов параметров в С++ и в .NET коде:
Тип C/C++ Тип .NET BOOL bool LPCTSTR string -
Командлет
Add-Type
создает классKernel32
, содержащий методCopyItem
, определение которого задано на предыдущем шаге. -
Последняя строка кода вызывает метод
Kernel32::CopyFile
для копирования файла calc.exe из папки Windows\System32 на рабочий стол.
Дальнейшие улучшения
- Редактирование C# кода в строковой константе внутри PowerShell скрипта может оказаться неудобным. Вы можете сохранить C# код в отдельный файл и получить контент файла с помощью командлета Get-Content.
- Вы можете объявить командлет
Copy-RawItem
в своем PowerShell модуле — чтобы облегчить повторное использование кода. - Вы можете реализовать обработку ошибок — генерировать исключение при неудачном завершении
CopyFile
.
Следующий ниже код демонстрирует реализацию модуля CopyRawItem.psm1 с учетом вышеизложенных доработок:
function Copy-RawItem {
[CmdletBinding()]
[OutputType([System.IO.FileSystemInfo])]
Param (
[Parameter(Mandatory = $True, Position = 0)]
[ValidateNotNullOrEmpty()]
[String]
$Path,
[Parameter(Mandatory = $True, Position = 1)]
[ValidateNotNullOrEmpty()]
[String]
$Destination,
[Switch]
$FailIfExists
)
$MethodDefinition = Get-Content -Path .\CopyFile.cs
$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -Namespace 'Win32' -PassThru
$CopyResult = $Kernel32::CopyFile($Path, $Destination, ([Bool] $PSBoundParameters['FailIfExists']))
if ($CopyResult -eq $False) {
throw New-Object ComponentModel.Win32Exception(
[System.Runtime.InteropServices.Marshal]::GetLastWin32Error())
}
else {
Write-Output (Get-ChildItem $Destination)
}
}
Вы можете импортировать этот модуль и вызвать Copy-RawItem
из вашего скрипта таким образом:
Import-Module ./CopyRawItem.psm1
Copy-RawItem \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SAM C:\temp\SAM -FailIfExists
В этом примере, файл базы данных диспетчера учётных записей безопасности копируется из теневой копии тома во временную папку (что невозможно при использовании стандартного командлета Copy-Item). Если запустить Copy-RawItem
дважды, вы получите ошибку “The file exists”:
При задании неверного пути выдается ошибка “The system cannot find the path specified”.
Совет
Если вы получаете ошибку “Access is denied” при доступе к теневой копии, перезапустите сеанс PowerShell от имени администратора.
Примечание
Использованный здесь код доступен на GitHub: powershell-winapi-tutorial/examples/add-type.