Skip to content

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:

PowerShell
$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 of CopyFile.

    Expand to see the details

    C++ signature
    BOOL 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 the Kernel32 class containing the CopyFile 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:

PowerShell Module
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:

PowerShell
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:

Copy-RawItem result

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.