Search code examples
powershellpinvokeunmanaged

Powershell - calling unmanaged Function CreateProfile crashes Powershell.exe


When calling a unmanaged piece of code with pinvoke -- createprofile. The Powershell.exe process crashes after the call to the method in the unmanaged code. The profile is created successfully.

Why this would happen? My code is below:

    function CreateProfile
    {
        param([String]$UserSid, [String]$UserName,  [system.uint32]$ProfilePath)
        Add-Type -TypeDefinition '
            using System;
            using System.Runtime.InteropServices;
            public static class PInvoke {
                [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
                public static extern int CreateProfile( [MarshalAs(UnmanagedType.LPWStr)] String pszUserSid, [MarshalAs(UnmanagedType.LPWStr)] String pszUserName, [Out][MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder pszProfilePath, uint cchProfilePath);
            }
        '
       $pszProfilePath = new-object -typename System.Text.StringBuilder
    [int]$results = [PInvoke]::CreateProfile($UserSid, $UserName, $pszProfilePath, $ProfilePath)
    }
$stringbuff = new-object system.text.stringbuilder(260)
[system.uint32]$a =$stringbuff.capacity
$sid = ((get-aduser -id 'brtestlocaluser').sid.value)
CreateProfile -usersid $sid -username 'brtestlocaluser' -ProfilePath $a

Solution

  • Finally was able to figure this out. Calling it in another means seemed to fix this issue.

    function Register-NativeMethod
    {
        [CmdletBinding()]
        [Alias()]
        [OutputType([int])]
        Param
        (
            # Param1 help description
            [Parameter(Mandatory=$true,
                       ValueFromPipelineByPropertyName=$true,
                       Position=0)]
            [string]$dll,
    
            # Param2 help description
            [Parameter(Mandatory=$true,
                       ValueFromPipelineByPropertyName=$true,
                       Position=1)]
            [string]
            $methodSignature
        )
    
        $script:nativeMethods += [PSCustomObject]@{ Dll = $dll; Signature = $methodSignature; }
    }
    function Add-NativeMethods
    {
        [CmdletBinding()]
        [Alias()]
        [OutputType([int])]
        Param($typeName = 'NativeMethods')
    
        $nativeMethodsCode = $script:nativeMethods | ForEach-Object { "
            [DllImport(`"$($_.Dll)`")]
            public static extern $($_.Signature);
        " }
    
        Add-Type @"
            using System;
            using System.Text;
            using System.Runtime.InteropServices;
            public static class $typeName {
                $nativeMethodsCode
            }
    "@
    }
    function New-ProfileFromSID {
    
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        # Param1 help description
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [string]$UserName,
        [string]$domain = ''
    )
    $methodname = 'UserEnvCP2'
    $script:nativeMethods = @();
    
    if (-not ([System.Management.Automation.PSTypeName]$methodname).Type)
    {
        Register-NativeMethod "userenv.dll" "int CreateProfile([MarshalAs(UnmanagedType.LPWStr)] string pszUserSid,`
         [MarshalAs(UnmanagedType.LPWStr)] string pszUserName,`
         [Out][MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszProfilePath, uint cchProfilePath)";
    
        Add-NativeMethods -typeName $methodname;
    }
    
    $sb = new-object System.Text.StringBuilder(260);
    $pathLen = $sb.Capacity;
    
    Write-Verbose "Creating user profile for $Username";
    #$SID= ((get-aduser -id $UserName -ErrorAction Stop).sid.value)
    if($domain)
    {
        $objUser = New-Object System.Security.Principal.NTAccount($domain, $UserName)
        $strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier])
        $SID = $strSID.Value
    }
    else 
    {
       $objUser = New-Object System.Security.Principal.NTAccount($UserName)
       $strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier])
       $SID = $strSID.Value
    }
    Write-Verbose "$UserName SID: $SID"
    try
    {
       $result = [UserEnvCP2]::CreateProfile($SID, $Username, $sb, $pathLen) 
       if($result -eq '-2147024713')
       {
           $status = "$userName already exists"
           write-verbose "$username Creation Result: $result"
        }
        elseif($result -eq '-2147024809')
        {
            $status = "$username Not Found"
            write-verbose "$username creation result: $result"
        }
       elseif($result -eq 0)
       {
           $status = "$username Profile has been created"
           write-verbose "$username Creation Result: $result"
       }
       else
       {
          $status = "$UserName unknown return result: $result"
       }
    }
    catch
    {
        Write-Error $_.Exception.Message;
        break;
    }
    $status
    }