Use the Add-Type Cmdlet that Calls the Windows API Function
The Add-Type cmdlet adds a specified .NET class to a PowerShell session. This topic demonstrates how to use this cmdlet to access the CopyFile function declared in the kernel32.dll library.
Basic Implementation
Consider the script that calls the CopyFile
function:
$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)
This script does the following:
-
The
$MethodDefinition
variable contains the ะก# method definition that matches the C++ signature ofCopyFile
.Expand to see the details
C++ signatureBOOL CopyFile( [in] LPCTSTR lpExistingFileName, [in] LPCTSTR lpNewFileName, [in] BOOL bFailIfExists );
C# definition[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool CopyFile( string lpExistingFileName, string lpNewFileName, bool bFailIfExists );
The DllImport attribute indicates that the
CopyFile
method is exposed by the kernel32.dll library as a static entry point.Note the correct translation of C++ parameter types to corresponding .NET types:
C/C++ Type .NET Type BOOL bool LPCTSTR string -
The
Add-Type
cmdlet creates theKernel32
class containing theCopyFile
method defined at the previous step. -
The last line of code calls the
Kernel32::CopyFile
method to copy the calc.exe file from Windows\System32 folder to the desktop.
Further Improvements
- It may be inconvenient to edit C# code as a string inside the PowerShell script. You can save the C# code to a separate file (e.g., CopyFile.cs) and get the file content using the Get-Content cmdlet.
- You can declare the
Copy-RawItem
commandlet in a custom PowerShell module file — to make your code reusable. - You can implement error handling — throw an exception when
CopyFile
fails.
The code below demonstrates the CopyRawItem.psm1 module implementation, with the above improvements:
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)
}
}
You can import this module and call Copy-RawItem
from your script as follows:
Import-Module ./CopyRawItem.psm1
Copy-RawItem \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SAM C:\temp\SAM -FailIfExists
Here, the security account manager database file is copied from the volume shadow copy to a temporary folder (which is impossible when using standard Copy-Item cmdlet). If you run Copy-RawItem
twice, you will see the “file exists” error:
If you specify an incorrect path, the “system cannot find the path specified” error is displayed.
Tip
If you receive the “Access is denied” error when accessing the shadow copy, run your PowerShell session as administrator.
Note
You can get the complete code demonstrated here at GitHub: powershell-winapi-tutorial/examples/add-type.